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

[Filters] [Config] [DI] Add Filter configuration class as public service #1098

Merged
merged 31 commits into from Jun 26, 2018

Conversation

maximgubar
Copy link
Contributor

@maximgubar maximgubar commented May 23, 2018

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets
License MIT
Doc PR

In this PR I propose to introduce a service, which retrieves filter_sets, filters configuration and exposes it via interfaces.

@maximgubar maximgubar requested a review from dbu May 23, 2018 09:08
@robfrawley
Copy link
Collaborator

robfrawley commented May 23, 2018

Overalls, this looks great.

I do have some ideas I've been wanting to implement regarding filter-sets/filter that would be good to bring up now. The number of times people confuse the difference between filter-sets and filters is countless, I submit we should change the terminology of filter-set to "instructions" or something different. Thoughts? Other naming ideas?

The "quality" setting is also going to be deprecated because we now have "png_compression_level" and "jpeg_quality" now instead, so this interface should likely accommodate those new options (this has already been done in the underlying imagine library).

Also, do we need interfaces for classes we're marking as final? It seems like possibly extra, unnecessary code to maintain if we aren't intending it to be an extension point.

@robfrawley
Copy link
Collaborator

robfrawley commented May 23, 2018

Also, with one of the PRs I'm converting from 1.x to 2.0 I actually created a "configuration" class as well (instead of adding tons of constructor arguments, as done in 1.x it seemed better to pass a config class), so perhaps we should move everything in this PR from Config/ to Config/Filter namespace so we can define other configs for other components under the same Config/ namespace (like the Config/Controller/ControllerConfig.php class from my work) without having tons of unrelated config classes together in the same namespace level.

Copy link
Member

@dbu dbu left a comment

Choose a reason for hiding this comment

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

good idea with expressing the configuration as objects. i think the basic structure is good.

as discussed before, i am not sure if the interfaces add all that much. though the implementation being final is not an argument against interfaces, actually the opposite. if somebody wants to change something, they have to implement the interface themselves. with the config classes being final, you can't adjust them. so overall i am ok to keep the interfaces.

could it make sense to make the filter classes specific to the type of filter they are? then instead of generic "options" we would have specific getters for width, height, blur factor, whatever the filter is doing... that would make the configuration much more explicit.

i would drop the setters from the interfaces, and also from the implementations and use constructor arguments instead, to make the configuration immutable. if we do that, it also makes sense to merge the factory and builder classes, as setting the information is no longer defined in the interface. imho that simplifies the codebase and makes it more clear.

people confuse the difference between filter-sets and filters is countless, I submit we should change the terminology

in rokka.io we call the filter-set "stack". what would you think of that name? oderwise, maybe "ImageFormat", as in the end it defines the format for one image. "instructions" is a bit weird as its just a different interpretation of what a filter is.

and should we call the thing now called filter a FilterDefinition? its more verbose, but in the context of the classes using these definitions, there are also the "actual" imagine filters which transform the image. this configuration container class only represents the definition for the filter, not the actual filter ;-)

The "quality" setting is also going to be deprecated because we now have "png_compression_level" and "jpeg_quality"

makes sense. is the configuration in the bundle already transformed in that way? do we currently use quality 1-10 for png and 100-1 for jpeg compression?

namespace

that makes sense to me, lets move the filters and stacks there.


namespace Liip\ImagineBundle\Config;

interface FilterInterface
Copy link
Member

Choose a reason for hiding this comment

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

i would like to do a phpdoc that explains what a filter is (the definition for one image transformation operation)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dbu could you provide an example ? cause I'm not sure that I understand this comment.

Copy link
Member

Choose a reason for hiding this comment

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

/**
 * A filter contains the configuration for an image transformation operation.
 *
 * The type of operation is defined by the filter class. The operation parameters are specific to
 * the filter.
 */

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

/**
* @param string $name
*/
public function setName(string $name): void;
Copy link
Member

Choose a reason for hiding this comment

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

imo the interface should not define setters. the whole definition is a read-only thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

completely agree with you :)

/**
* @return int
*/
public function getQuality(): int
Copy link
Member

Choose a reason for hiding this comment

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

this should have a phpdoc explaining what it is. (resp, as rob says the jpg quality and the png compression level values should have that doc, to explain the value range)

/**
* @param string $name
*/
public function setName(string $name): void
Copy link
Member

Choose a reason for hiding this comment

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

i'd rather use constructor arguments than setters

Copy link
Contributor Author

Choose a reason for hiding this comment

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

got it, will refactor it.

@robfrawley
Copy link
Collaborator

robfrawley commented May 24, 2018

  • "Stacks" isn't a bad name at all. I'm fine with stack, transformation_stack, instructions, instruction_stack, named_transformations, stack_definition, transformation_definition, etc. Not too picky, as long as it isn't filter_sets anymore (and ImageFormat seems like it too would cause confusion: they don't just define the image format, but the operations to perform on the image data, the loading/caching mechanism, etc). Once we settle on a good name here we can deprecate the current bundle configuration entry, provide a new one, and handle normalization for BC until 3.0.

  • I think FilterDefinition/StackDefinition might be a good way to differentiate between config classes and actual filter implementations (or even FilterConfig/StackConfig or another variant). There are already a bunch of classes and/or interfaces similarly or exactly named the same as others, in different namespaces, of course, but it always causes issues and confusion for newcomers. We should attempt to avoid creating new overlap as much as possible.

  • I don't think I was clear about my position on "final"/"interfaces" prior: let's try it again. :-) What I was trying to point out is that unless we intend for this to be an extension point, which I don't currently see any real use-cases for (but do correct me if I'm wrong) we should: 1. mark them as final, and 2. not provide interfaces. In other words, we should publish a working version of this change with no extension points, recieved feedback and bug reports, stabalized the API, and wait for feedback that extending these classes is even desirable to our users. If our users don't even want or need it, the only thing providing interfaces at this time accomplishes is locking us to a contract that we'll have to maintain and we'll have to stick to indefinetely. Until 3.0 at the earliest, anyway.

    I've already made that mistake with earlier contributions and PRs I've merged from others; since, I've tried to limit interfaces at all costs and expand final usage. It makes things easier, especially early in a feature's lifetime. Anyway, just my two cents. :-)

  • If you are really stuck on the inclusion of interfaces, perhaps we should introduce an @experimental tag, like Symfony uses, to mark sections of code as pre-release and not production-API stable?

  • This bundle is currently supporting all quality factors: the deprecated quality option (1-100), the new png_compression_level option (0-9), and the new jpeg_quality option (1-100). At some point we need to throw a deprecation on usage of quality and normalize/map it to the two new options automatically.

  • After you've gotten a final PR together, do take a look at the coveralls code coverage data to ensure you're properly all testing the new code; it looks like the coverage percentage droped a bit with this PR.

Anyway, thanks for all the hard work @maximgubar. The PR looks solid and I'm excited to get it included.

@robfrawley robfrawley changed the title added FilterSetCollection public service [Filters] [Config] [DI] Add Filter configuration class as public service May 27, 2018
Copy link
Member

@dbu dbu left a comment

Choose a reason for hiding this comment

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

nice, i like it! i would love to bring down the number of interfaces and meta infrastructure classes a bit, see some of my comments.


/**
* @param string $name
* @param array $size
Copy link
Member

Choose a reason for hiding this comment

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

can you specify what kind of array this is? is it a hashmap with width and height?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

interface FilterFactoryInterface
{
/**
* @return string
Copy link
Member

Choose a reason for hiding this comment

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

can we say that the name must match the Filter::getName that this factory handles?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

private $filterFactories;

/**
* FilterCollection constructor.
Copy link
Member

Choose a reason for hiding this comment

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

i'd remove docblocks that don't add any information over whats in the php code

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

*/
public function __construct(FilterFactoryInterface ...$filterFactories)
{
$this->filterFactories = $filterFactories;
Copy link
Member

Choose a reason for hiding this comment

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

imho we should convert this to a hashmap with name => factory so that we don't have to loop over the array in getFilterFactoryByName

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


use Liip\ImagineBundle\Factory\Config\FilterFactoryInterface;

interface FilterFactoryCollectionInterface
Copy link
Member

Choose a reason for hiding this comment

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

imho there is only one way how this can be implemented and hence no point in having an interface

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

<service id="Liip\ImagineBundle\Factory\Config\Filter\StripFactory" alias="liip_imagine.factory.config.filter.strip"/>

<service id="liip_imagine.factory.config.filter.thumbnail" class="Liip\ImagineBundle\Factory\Config\Filter\ThumbnailFactory" />
<service id="Liip\ImagineBundle\Factory\Config\Filter\ThumbnailFactory" alias="liip_imagine.factory.config.filter.thumbnail"/>
Copy link
Member

Choose a reason for hiding this comment

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

i don't think that we need an autowiring capable alias for these. they should be private services not useable outside of DI configuration

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

<argument type="service" id="liip_imagine.factory.config.filter.strip"/>
<argument type="service" id="liip_imagine.factory.config.filter.thumbnail"/>
</service>
<service id="Liip\ImagineBundle\Config\FilterFactoryCollection" alias="liip_imagine.config.filter_factory_collection"/>
Copy link
Member

Choose a reason for hiding this comment

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

neither do we need autowiring for this. the only thing that should be available for autowiring should be the actual configuration object.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dbu, btw, do we need to have SF4 compatibility ?

Copy link
Member

Choose a reason for hiding this comment

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

it would be nice, but i would not mix it into this pull request.

if we have new services that should be autowire-enabled, we can already define the right alias here, but i would not do anything outside the scope of modeling the configuration.

* @param FilterConfiguration $filterConfiguration
* @param FilterSetBuilderInterface $filterSetBuilder
*/
public function __construct(FilterConfiguration $filterConfiguration, FilterSetBuilderInterface $filterSetBuilder)
Copy link
Member

Choose a reason for hiding this comment

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

does it make sense to use the FilterConfiguration object? looks to me like that is a previous attempt at this problem that did not go far enough? it smells like we should deprecate FilterConfiguration and pass the plain array here, or not?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


/**
* @param FilterConfiguration $filterConfiguration
* @param FilterSetBuilderInterface $filterSetBuilder
Copy link
Member

Choose a reason for hiding this comment

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

having factories for so many things, why do we not have a factory for the StackCollection? we could then define the public service for this as a thing that uses the StackCollectionFactory, and not have the value object that is used outside have the factory in it. the FilterSetBuilder could take that role, to not explode the number of infrastructure classes that we use.

for the symfony config, it would look like this: https://symfony.com/doc/current/service_container/factories.html


namespace Liip\ImagineBundle\Config;

final class Filter implements FilterInterface
Copy link
Member

Choose a reason for hiding this comment

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

once you implemented all filters, this will become obsolete, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

/**
* @return string
*/
public function getName(): string
Copy link
Contributor

Choose a reason for hiding this comment

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

Max, this is wrong. If color is optional - use ?string as return type. Also phpdocs which duplicates actual typehints/return types are useless.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks for the hint, will refactor.


/**
* @param string $name
* @param array $start start coordinates {x,y}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not provide value object for Point?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good idea, will add it

/**
* @param FilterFactoryInterface ...$filterFactories
*/
public function __construct(FilterFactoryInterface ...$filterFactories)
Copy link
Contributor

Choose a reason for hiding this comment

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

Variadic here looks so strange. IMHO it is better use array of FilterFactoryInterface and instanceof/private method to ensure type of each item in array.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

got your point, but how usage of array and additional checks of type is better ?
since this object actually plays a role of collection and it is constructed with FilterFactoryInterface(s) why just not declare it directly ?

Copy link
Member

Choose a reason for hiding this comment

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

as you loop over the parameter anyways, you could also do an instanceof check, and have the @param FilterFactoryInterface[] $filterFactories to allow code sniffers to detect errors.

if we ever need to add an (optional) constructor argument, the current form will be a problem as we can't add anything else without a BC break.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

why would we ever need to to add something to constructor of this class ?
the purpose of this object is to store FilterFactoryInterfaces only and thats it. it shouldn't contain any logic. tbh, in this case specifically, I don't see advantages of array implementation.

Copy link
Member

Choose a reason for hiding this comment

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

i don't mind much either way.

thinking of it, we should mark all the factory part as @internal to tell people to not use that in their project. the exposed thing is the configuration object model, not the factories to build it. if we do that we can also leave the variadic imho.

Copy link
Member

Choose a reason for hiding this comment

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

yes, because final means you can't extend the class, but @internal means you should not interact with it at all, e.g. instantiate it in your code and so something with it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess within Symfony the pattern used most often is an add() method to build the collection instead of a constructor argument. obviously this has more performance overhead however. I think this is an interesting approach but since variadic in user land is still a new concept there is less experience with it.

but I wonder if it wouldn't be better to use a factory method instead of the constructor. ie. something like:

class FilterFactoryCollection
{
    static public function createFromList(FilterFactoryInterface ...$filterFactories)
    {
        $collection = new self;
        $collection->addList(FilterFactoryInterface ...$filterFactories);
    }
}

this keeps the options open for if we ever do need to add anything to the constructor. while keeping the convenience and more or less the overhead reduction benefits.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this object has only one purpose, to store the FilterFactoryInterfaces (which are clearly its dependencies and should be specified), I dont see any valid case which would lead to altering constructor. Also, I am not sure is it a good practice to do indirect object injections (via setters).

Copy link
Contributor

Choose a reason for hiding this comment

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

ok lets keep it as proposed. like I said, a good chance of this feeling "weird" is simply the lack of experience with variadic in user land .. and honestly this is not the most "risky" place .. if we do in the end realize we need support additional constructor parameters .. we can then still use a static factory method for that case.

Copy link
Member

@dbu dbu left a comment

Choose a reason for hiding this comment

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

added a bunch of small review comments. but i like where this is going, i think its coming to a good form.


/**
* @param string $name
* @param string|null $color
Copy link
Member

Choose a reason for hiding this comment

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

i am ok with using a string for the $color, but we should explain here what it is. RRGGBB? with optional transparency?

/**
* @param string $name
* @param string|null $color
* @param string|null $transparency
Copy link
Member

Choose a reason for hiding this comment

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

we should state the range of this. 0 to 100? or 0 to 1? or something else?

* @param string $name
* @param string|null $color
* @param string|null $transparency
* @param string|null $position
Copy link
Member

Choose a reason for hiding this comment

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

is that a list of keywords like TOP|LEFT|BOTTOM|RIGHT, or something else?

* @param string|null $color
* @param string|null $transparency
* @param string|null $position
* @param array $size
Copy link
Member

Choose a reason for hiding this comment

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

keys x and y? or do we use a value object for this?

/**
* @return float|null
*/
public function getBy()
Copy link
Member

Choose a reason for hiding this comment

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

: ?float instead of the docblock

*/
public function create(array $options): FilterInterface
{
$color = isset($options['color']) ? $options['color'] : null;
Copy link
Member

Choose a reason for hiding this comment

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

the most condensed for these checks would be $color = $options['color'] ?? null;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

is it a typo ?
should it be like this ?
$color = $options['color'] ?: null;

Copy link
Member

Choose a reason for hiding this comment

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

no. that will generate a warning when the index 'color' is not set. ?? is the null coalesce operator introduced in PHP 7 to shorten exactly the construct you used above.

public function create(array $options): FilterInterface
{
$mode = ImageInterface::INTERLACE_LINE;
if (!empty($options['mode'])) {
Copy link
Member

Choose a reason for hiding this comment

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

why empty and not isset? i'd go with the pattern $mode = $options['mode'] ?? ImageInterface::INTERLACE_LINE;

public function create(array $options): FilterInterface
{
$size = [];
if (isset($options['size']) && is_array($options['size'])) {
Copy link
Member

Choose a reason for hiding this comment

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

this is silently ignoring non-array size options, which is a problem imho. we should do the $size = $options['size'] ?? []; . the constructor type declaration on $size will detect if the size option has been set to an invalid value.

* @param string $name
* @param string|null $dataLoader
* @param int|null $quality
* @param array $filters
Copy link
Member

Choose a reason for hiding this comment

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

FilterInterface[]

*/
public function create(string $name, string $dataLoader = null, int $quality = null, array $filters)
{
return new Stack($name, $dataLoader, $quality, $filters);
Copy link
Member

Choose a reason for hiding this comment

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

also, is there value to use a factory for this over just doing new Stack() in the place where we need the stack? feels to me like the factory is too much abstraction.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

using new is breaking DI principle. it makes it hardcoded and non-extendable and not testable.

Copy link
Member

Choose a reason for hiding this comment

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

i don't see the problem. we don't want a mock stack anywhere, its a value object. to me it looks overengineered with this. we do not need to DI what kind of stack is created as its just a value object. but its not a huge deal either.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we already mock it in our unit tests

Copy link
Member

Choose a reason for hiding this comment

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

i don't see the need for mocking the Stack object in a unit test, its straightforward enough imho. yes, its not a perfectly atomic unit test, but good enough as all of the build process is just infrastructure code to build the configuration object.

* @param string|null $position
* @param string|null $color background color HEX value
* @param string|null $transparency possible values 0..100
* @param string|null $position position of the input image on the newly created background image. Valid values: topleft, top, topright, left, center, right, bottomleft, bottom, and bottomright
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we can use value objects/enum here also.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Koc for all parameters or for specific one ?

Copy link
Member

Choose a reason for hiding this comment

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

i would not use value objects here. if the imagine library would use them, we should use them here too, but as they don't i feel like its a bit too much.

*/
private $height;

public function __construct(int $width = null, int $height = null)
Copy link
Contributor

@Koc Koc Jun 6, 2018

Choose a reason for hiding this comment

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

Does size without width and height has any business value? Same for point. It looks like incomplete/invalid object. If Size is optional for some operations - maybe se can use typehing ?Size in this operations

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Koc there are cases, when one of the dimensions could be null, how would you suggest to implement it if this approach causes doubts ?

Copy link
Member

Choose a reason for hiding this comment

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

Lets add a phpdoc to this method? "To allow keeping aspect ratio, it is allowed to only specify one of width or height. It is however not allowed to specify neither dimension."

Copy link
Member

@dbu dbu left a comment

Choose a reason for hiding this comment

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

there are some comments that seem to have slipped through. and some more phpdoc that we can remove because it does not provide extra information.

the only larger bit i don't like is that the filter names are arguments and the constant is in the factories. i would instead put the constant into the filter class and reference it from the factory, and not have a constructor argument $name in the filters, but use the constant in getName

private $name;

/**
* @param string $name
Copy link
Member

Choose a reason for hiding this comment

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

lets remove this, its redundant


/**
* @param string $name
* @param string $mode
Copy link
Member

Choose a reason for hiding this comment

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

^

* @param float|null $heighten
* @param float|null $widen
* @param float|null $increase
* @param float|null $scale
Copy link
Member

Choose a reason for hiding this comment

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

please remove this phpdoc as it does not add anything

private $name;

/**
* @param string $name
Copy link
Member

Choose a reason for hiding this comment

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

please remove

* @param Size $size
* @param string|null $mode
* @param bool|null $allowUpscale
* @param string|null $filter
Copy link
Member

Choose a reason for hiding this comment

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

please remove the docblock as it contains no information

* @param string $filterSetName
* @param array $filterSetData
*
* @return StackInterface
Copy link
Member

Choose a reason for hiding this comment

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

please remove, this is all visible in the method signature

final class StackCollection
{
/**
* @var array
Copy link
Member

Choose a reason for hiding this comment

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

^

/**
* @var array
*/
private $filterSets = [];
Copy link
Member

Choose a reason for hiding this comment

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

^


/**
* @param StackBuilderInterface $stackBuilder
* @param array $filtersConfiguration
Copy link
Member

Choose a reason for hiding this comment

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

please remove, its all in the method signature

/**
* @return string|null
*/
public function getDataLoader();
Copy link
Member

Choose a reason for hiding this comment

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

as we are on php 7.1 or better, please use the return type declaration getDataLoader(): ?string and remove the phpdoc. same for getQuality

/**
* @codeCoverageIgnore
*/
final class Background extends FilterAbstract implements FilterInterface
Copy link
Member

Choose a reason for hiding this comment

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

you can remove the implements FilterInterface because thats already given by extending FilterAbstract

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

* @param string $filterSetName
* @param array $filterSetData
*
* @return StackInterface
Copy link
Member

Choose a reason for hiding this comment

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

this does not add anything over the phpdoc, lets remove it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

interface FilterFactoryInterface
{
/**
* Filter name
Copy link
Member

Choose a reason for hiding this comment

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

^

* @param int|null $quality
* @param FilterInterface[] $filters
*
* @return Stack
Copy link
Member

Choose a reason for hiding this comment

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

could we instead do the return type declaration on the method itself?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Member

@dbu dbu left a comment

Choose a reason for hiding this comment

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

i don't see any further things that i would want to change. i vote for merge (squashing commits). @robfrawley are you okay with that?

@dbu
Copy link
Member

dbu commented Jun 18, 2018

@robfrawley i would prefer to have some final feedback from you before we merge this. do you have time to look at this soonish?

if (false === mb_strpos($path, '..'.DIRECTORY_SEPARATOR) &&
false === mb_strpos($path, DIRECTORY_SEPARATOR.'..') &&
false !== file_exists($absolute = $root.DIRECTORY_SEPARATOR.$path)
if (false === mb_strpos($path, '..'.\DIRECTORY_SEPARATOR) &&
Copy link
Contributor

Choose a reason for hiding this comment

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

why the backslashes here?

@@ -60,7 +60,7 @@ public function locate(string $path): string
*/
protected function generateAbsolutePath(string $root, string $path): ?string
{
if (false !== $absolute = realpath($root.DIRECTORY_SEPARATOR.$path)) {
if (false !== $absolute = realpath($root.\DIRECTORY_SEPARATOR.$path)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

why the backslashes here?

@@ -127,7 +127,7 @@ private function getBundleResourcePaths(ContainerBuilder $container)
}

return array_map(function ($path) {
return $path.DIRECTORY_SEPARATOR.'Resources'.DIRECTORY_SEPARATOR.'public';
return $path.\DIRECTORY_SEPARATOR.'Resources'.\DIRECTORY_SEPARATOR.'public';
Copy link
Contributor

Choose a reason for hiding this comment

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

why the backslashes here?

@@ -50,8 +50,8 @@

protected function setUp()
{
$this->fixturesPath = realpath(__DIR__.DIRECTORY_SEPARATOR.'Fixtures');
$this->temporaryPath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'liip_imagine_test';
$this->fixturesPath = realpath(__DIR__.\DIRECTORY_SEPARATOR.'Fixtures');
Copy link
Contributor

Choose a reason for hiding this comment

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

why the backslashes here?

/**
* @param FilterFactoryInterface ...$filterFactories
*/
public function __construct(FilterFactoryInterface ...$filterFactories)
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess within Symfony the pattern used most often is an add() method to build the collection instead of a constructor argument. obviously this has more performance overhead however. I think this is an interesting approach but since variadic in user land is still a new concept there is less experience with it.

but I wonder if it wouldn't be better to use a factory method instead of the constructor. ie. something like:

class FilterFactoryCollection
{
    static public function createFromList(FilterFactoryInterface ...$filterFactories)
    {
        $collection = new self;
        $collection->addList(FilterFactoryInterface ...$filterFactories);
    }
}

this keeps the options open for if we ever do need to add anything to the constructor. while keeping the convenience and more or less the overhead reduction benefits.

@lsmith77
Copy link
Contributor

maybe we should have a 2.1 branch for this? or should we actually start using master again for future minor/major new versions?

@maximgubar maximgubar changed the base branch from 2.0 to master June 26, 2018 08:25
@maximgubar
Copy link
Contributor Author

@lsmith77 we started to use master branch again, the base branch of this PR was changed.

@lsmith77 lsmith77 closed this Jun 26, 2018
@lsmith77 lsmith77 reopened this Jun 26, 2018
@lsmith77 lsmith77 merged commit 82aeb77 into master Jun 26, 2018
@lsmith77 lsmith77 deleted the config-reader branch June 26, 2018 11:51
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.

None yet

5 participants