Completed
Pull Request — 2.0 (#1094)
by Rob
01:54
created

FileSystemLoaderFactory::addConfiguration()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 47
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 47
rs 9.0303
c 0
b 0
f 0
cc 1
eloc 44
nc 1
nop 1
1
<?php
2
3
/*
4
 * This file is part of the `liip/LiipImagineBundle` project.
5
 *
6
 * (c) https://github.com/liip/LiipImagineBundle/graphs/contributors
7
 *
8
 * For the full copyright and license information, please view the LICENSE.md
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Liip\ImagineBundle\DependencyInjection\Factory\Loader;
13
14
use Liip\ImagineBundle\Exception\InvalidArgumentException;
15
use Liip\ImagineBundle\Utility\Framework\SymfonyFramework;
16
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
17
use Symfony\Component\DependencyInjection\ChildDefinition;
18
use Symfony\Component\DependencyInjection\ContainerBuilder;
19
20
class FileSystemLoaderFactory extends AbstractLoaderFactory
21
{
22
    /**
23
     * {@inheritdoc}
24
     */
25
    public function create(ContainerBuilder $container, $loaderName, array $config)
26
    {
27
        $locatorDefinition = new ChildDefinition(sprintf('liip_imagine.binary.locator.%s', $config['locator']));
28
        $locatorDefinition->replaceArgument(0, $this->resolveDataRoots($config['data_root'], $config['bundle_resources'], $container));
29
        $locatorDefinition->replaceArgument(1, $config['allow_unresolvable_data_roots']);
30
31
        $definition = $this->getChildLoaderDefinition();
32
        $definition->replaceArgument(2, $locatorDefinition);
33
34
        return $this->setTaggedLoaderDefinition($loaderName, $definition, $container);
35
    }
36
37
    /**
38
     * {@inheritdoc}
39
     */
40
    public function getName()
41
    {
42
        return 'filesystem';
43
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48
    public function addConfiguration(ArrayNodeDefinition $builder)
49
    {
50
        $builder
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Config...\Builder\NodeDefinition as the method prototype() does only exist in the following sub-classes of Symfony\Component\Config...\Builder\NodeDefinition: Symfony\Component\Config...der\ArrayNodeDefinition. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
51
            ->children()
52
                ->enumNode('locator')
53
                    ->values(['filesystem', 'filesystem_insecure'])
54
                    ->info('Using the "filesystem_insecure" locator is not recommended due to a less secure resolver mechanism, but is provided for those using heavily symlinked projects.')
55
                    ->defaultValue('filesystem')
56
                ->end()
57
                ->arrayNode('data_root')
58
                    ->beforeNormalization()
59
                    ->ifString()
60
                        ->then(function ($value) {
61
                            return [$value];
62
                        })
63
                    ->end()
64
                    ->treatNullLike([])
65
                    ->treatFalseLike([])
66
                    ->defaultValue([SymfonyFramework::getContainerResolvableRootWebPath()])
67
                    ->prototype('scalar')
68
                        ->cannotBeEmpty()
69
                    ->end()
70
                ->end()
71
                ->booleanNode('allow_unresolvable_data_roots')
72
                    ->defaultFalse()
73
                ->end()
74
                ->arrayNode('bundle_resources')
75
                    ->addDefaultsIfNotSet()
76
                    ->children()
77
                        ->booleanNode('enabled')
78
                            ->defaultFalse()
79
                        ->end()
80
                        ->enumNode('access_control_type')
81
                            ->values(['blacklist', 'whitelist'])
82
                            ->info('Sets the access control method applied to bundle names in "access_control_list" into a blacklist or whitelist.')
83
                            ->defaultValue('blacklist')
84
                        ->end()
85
                        ->arrayNode('access_control_list')
86
                            ->defaultValue([])
87
                            ->prototype('scalar')
88
                                ->cannotBeEmpty()
89
                            ->end()
90
                        ->end()
91
                    ->end()
92
                ->end()
93
            ->end();
94
    }
95
96
    /*
97
     * @param string[]         $staticPaths
98
     * @param array            $config
99
     * @param ContainerBuilder $container
100
     *
101
     * @return string[]
102
     */
103
    private function resolveDataRoots(array $staticPaths, array $config, ContainerBuilder $container)
104
    {
105
        if (false === $config['enabled']) {
106
            return $staticPaths;
107
        }
108
109
        $resourcePaths = [];
110
111
        foreach ($this->getBundleResourcePaths($container) as $name => $path) {
112
            if (('whitelist' === $config['access_control_type']) === in_array($name, $config['access_control_list'], true) && is_dir($path)) {
113
                $resourcePaths[$name] = $path;
114
            }
115
        }
116
117
        return array_merge($staticPaths, $resourcePaths);
118
    }
119
120
    /**
121
     * @param ContainerBuilder $container
122
     *
123
     * @return string[]
124
     */
125
    private function getBundleResourcePaths(ContainerBuilder $container)
126
    {
127
        if ($container->hasParameter('kernel.bundles_metadata')) {
128
            $paths = $this->getBundlePathsUsingMetadata($container->getParameter('kernel.bundles_metadata'));
129
        } else {
130
            $paths = $this->getBundlePathsUsingNamedObj($container->getParameter('kernel.bundles'));
131
        }
132
133
        return array_map(function ($path) {
134
            return $path.DIRECTORY_SEPARATOR.'Resources'.DIRECTORY_SEPARATOR.'public';
135
        }, $paths);
136
    }
137
138
    /**
139
     * @param array[] $metadata
140
     *
141
     * @return string[]
142
     */
143
    private function getBundlePathsUsingMetadata(array $metadata)
144
    {
145
        return array_combine(array_keys($metadata), array_map(function ($data) {
146
            return $data['path'];
147
        }, $metadata));
148
    }
149
150
    /**
151
     * @param string[] $classes
152
     *
153
     * @return string[]
154
     */
155
    private function getBundlePathsUsingNamedObj(array $classes)
156
    {
157
        $paths = [];
158
159
        foreach ($classes as $c) {
160
            try {
161
                $r = new \ReflectionClass($c);
162
            } catch (\ReflectionException $exception) {
163
                throw new InvalidArgumentException(sprintf('Unable to resolve bundle "%s" while auto-registering bundle resource paths.', $c), null, $exception);
164
            }
165
166
            $paths[$r->getShortName()] = dirname($r->getFileName());
167
        }
168
169
        return $paths;
170
    }
171
}
172