Completed
Push — master ( 02e816...b5f511 )
by André
12:31
created

Configuration   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 429
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
dl 0
loc 429
rs 10
c 0
b 0
f 0
wmc 22
lcom 1
cbo 7

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A getConfigTreeBuilder() 0 17 1
C addRepositoriesSection() 0 133 11
B addSiteaccessSection() 0 89 4
B addImageMagickSection() 0 45 2
B addHttpCacheSection() 0 43 1
B addPageSection() 0 46 1
B addRouterSection() 0 26 1
1
<?php
2
3
/**
4
 * File containing the Configuration class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection;
10
11
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ParserInterface;
12
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\Configuration as SiteAccessConfiguration;
13
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorInterface;
14
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
15
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
16
17
class Configuration extends SiteAccessConfiguration
18
{
19
    /**
20
     * @var \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ParserInterface
21
     */
22
    private $mainConfigParser;
23
24
    /**
25
     * @var Configuration\Suggestion\Collector\SuggestionCollectorInterface
26
     */
27
    private $suggestionCollector;
28
29
    public function __construct(ParserInterface $mainConfigParser, SuggestionCollectorInterface $suggestionCollector)
30
    {
31
        $this->suggestionCollector = $suggestionCollector;
32
        $this->mainConfigParser = $mainConfigParser;
33
    }
34
35
    /**
36
     * Generates the configuration tree builder.
37
     *
38
     * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
39
     */
40
    public function getConfigTreeBuilder()
41
    {
42
        $treeBuilder = new TreeBuilder();
43
        $rootNode = $treeBuilder->root('ezpublish');
44
45
        $this->addRepositoriesSection($rootNode);
46
        $this->addSiteaccessSection($rootNode);
47
        $this->addImageMagickSection($rootNode);
48
        $this->addHttpCacheSection($rootNode);
49
        $this->addPageSection($rootNode);
50
        $this->addRouterSection($rootNode);
51
52
        // Delegate SiteAccess config to configuration parsers
53
        $this->mainConfigParser->addSemanticConfig($this->generateScopeBaseNode($rootNode));
54
55
        return $treeBuilder;
56
    }
57
58
    public function addRepositoriesSection(ArrayNodeDefinition $rootNode)
59
    {
60
        $rootNode
61
            ->children()
62
                ->arrayNode('repositories')
63
                    ->info('Content repositories configuration')
64
                    ->example(
65
                        array(
66
                            'main' => array(
67
                                'storage' => array(
68
                                    'engine' => 'legacy',
69
                                    'connection' => 'my_doctrine_connection_name',
70
                                ),
71
                            ),
72
                        )
73
                    )
74
                    ->useAttributeAsKey('alias')
75
                    ->prototype('array')
76
                        ->beforeNormalization()
77
                            ->always(
78
                                // Handling deprecated structure by mapping it to new one
79
                                function ($v) {
80
                                    if (isset($v['storage'])) {
81
                                        return $v;
82
                                    }
83
84
                                    if (isset($v['engine'])) {
85
                                        $v['storage']['engine'] = $v['engine'];
86
                                        unset($v['engine']);
87
                                    }
88
89
                                    if (isset($v['connection'])) {
90
                                        $v['storage']['connection'] = $v['connection'];
91
                                        unset($v['connection']);
92
                                    }
93
94
                                    if (isset($v['config'])) {
95
                                        $v['storage']['config'] = $v['config'];
96
                                        unset($v['config']);
97
                                    }
98
99
                                    return $v;
100
                                }
101
                            )
102
                        ->end()
103
                        ->beforeNormalization()
104
                            ->always(
105
                                // Setting default values
106
                                function ($v) {
107
                                    if ($v === null) {
108
                                        $v = array();
109
                                    }
110
111
                                    if (!isset($v['storage'])) {
112
                                        $v['storage'] = array();
113
                                    }
114
115
                                    if (!isset($v['search'])) {
116
                                        $v['search'] = array();
117
                                    }
118
119
                                    if (!isset($v['fields_groups']['list'])) {
120
                                        $v['fields_groups']['list'] = ['content'];
121
                                    }
122
123
                                    if (!isset($v['fields_groups']['default'])) {
124
                                        $v['fields_groups']['default'] = 'content';
125
                                    }
126
127
                                    if (!isset($v['options'])) {
128
                                        $v['options'] = [];
129
                                    }
130
131
                                    return $v;
132
                                }
133
                            )
134
                        ->end()
135
                        ->children()
136
                            ->arrayNode('storage')
137
                                ->children()
138
                                    ->scalarNode('engine')
139
                                        ->defaultValue('%ezpublish.api.storage_engine.default%')
140
                                        ->info('The storage engine to use')
141
                                    ->end()
142
                                    ->scalarNode('connection')
143
                                        ->defaultNull()
144
                                        ->info('The connection name, if applicable (e.g. Doctrine connection name). If not set, the default connection will be used.')
145
                                    ->end()
146
                                    ->arrayNode('config')
147
                                        ->info('Arbitrary configuration options, supported by your storage engine')
148
                                        ->useAttributeAsKey('key')
149
                                        ->prototype('variable')->end()
150
                                    ->end()
151
                                ->end()
152
                            ->end()
153
                            ->arrayNode('search')
154
                                ->children()
155
                                    ->scalarNode('engine')
156
                                        ->defaultValue('%ezpublish.api.search_engine.default%')
157
                                        ->info('The search engine to use')
158
                                    ->end()
159
                                    ->scalarNode('connection')
160
                                        ->defaultNull()
161
                                        ->info('The connection name, if applicable (e.g. Doctrine connection name). If not set, the default connection will be used.')
162
                                    ->end()
163
                                    ->arrayNode('config')
164
                                        ->info('Arbitrary configuration options, supported by your search engine')
165
                                        ->useAttributeAsKey('key')
166
                                        ->prototype('variable')->end()
167
                                    ->end()
168
                                ->end()
169
                            ->end()
170
                            ->arrayNode('fields_groups')
171
                                ->info('Definitions of fields groups.')
172
                                ->children()
173
                                    ->arrayNode('list')->prototype('scalar')->end()->end()
174
                                    ->scalarNode('default')->defaultValue('content')->end()
175
                                ->end()
176
                            ->end()
177
                            ->arrayNode('options')
178
                                ->info('Options for repository.')
179
                                ->children()
180
                                    ->scalarNode('default_version_archive_limit')
181
                                        ->defaultValue(5)
182
                                        ->info('Default version archive limit (0-50), only enforced on publish, not on un-publish.')
183
                                    ->end()
184
                                ->end()
185
                            ->end()
186
                        ->end()
187
                    ->end()
188
                ->end()
189
            ->end();
190
    }
191
192
    public function addSiteaccessSection(ArrayNodeDefinition $rootNode)
193
    {
194
        $rootNode
195
            ->children()
196
                ->arrayNode('siteaccess')
197
                    ->info('SiteAccess configuration')
198
                    ->children()
199
                        ->arrayNode('list')
200
                            ->info('Available SiteAccess list')
201
                            ->example(array('ezdemo_site', 'ezdemo_site_admin'))
202
                            ->isRequired()
203
                            ->requiresAtLeastOneElement()
204
                            ->prototype('scalar')->end()
205
                        ->end()
206
                        ->arrayNode('groups')
207
                            ->useAttributeAsKey('key')
208
                            ->info('SiteAccess groups. Useful to share settings between Siteaccess')
209
                            ->example(array('ezdemo_group' => array('ezdemo_site', 'ezdemo_site_admin')))
210
                            ->prototype('array')
211
                                ->requiresAtLeastOneElement()
212
                                ->prototype('scalar')->end()
213
                            ->end()
214
                        ->end()
215
                        ->scalarNode('default_siteaccess')->isRequired()->info('Name of the default siteaccess')->end()
216
                        ->arrayNode('match')
217
                            ->info('Siteaccess match configuration. First key is the matcher class, value is passed to the matcher. Key can be a service identifier (prepended by "@"), or a FQ class name (prepended by "\\")')
218
                            ->example(
219
                                array(
220
                                    'Map\\URI' => array(
221
                                        'foo' => 'ezdemo_site',
222
                                        'ezdemo_site' => 'ezdemo_site',
223
                                        'ezdemo_site_admin' => 'ezdemo_site_admin',
224
                                    ),
225
                                    'Map\\Host' => array(
226
                                        'ezpublish.dev' => 'ezdemo_site',
227
                                        'admin.ezpublish.dev' => 'ezdemo_site_admin',
228
                                    ),
229
                                    '\\My\\Custom\\Matcher' => array(
230
                                        'some' => 'configuration',
231
                                    ),
232
                                    '@my.custom.matcher' => array(
233
                                        'some' => 'other_configuration',
234
                                    ),
235
                                )
236
                            )
237
                            ->isRequired()
238
                            ->useAttributeAsKey('key')
239
                            ->normalizeKeys(false)
240
                            ->prototype('array')
241
                                ->useAttributeAsKey('key')
242
                                ->beforeNormalization()
243
                                    ->always(
244
                                        function ($v) {
245
                                            // Value passed to the matcher should always be an array.
246
                                            // If value is not an array, we transform it to a hash, with 'value' as key.
247
                                            if (!is_array($v)) {
248
                                                return array('value' => $v);
249
                                            }
250
251
                                            // If passed value is a numerically indexed array, we must convert it into a hash.
252
                                            // See https://jira.ez.no/browse/EZP-21876
253
                                            if (array_keys($v) === range(0, count($v) - 1)) {
254
                                                $final = array();
255
                                                foreach ($v as $i => $val) {
256
                                                    $final["i$i"] = $val;
257
                                                }
258
259
                                                return $final;
260
                                            }
261
262
                                            return $v;
263
                                        }
264
                                    )
265
                                ->end()
266
                                ->normalizeKeys(false)
267
                                ->prototype('variable')->end()
268
                            ->end()
269
                        ->end()
270
                    ->end()
271
                ->end()
272
                ->arrayNode('locale_conversion')
273
                    ->info('Locale conversion map between eZ Publish format (i.e. fre-FR) to POSIX (i.e. fr_FR). The key is the eZ Publish locale. Check locale.yml in EzPublishCoreBundle to see natively supported locales.')
274
                    ->example(array('fre-FR' => 'fr_FR'))
275
                    ->useAttributeAsKey('key')
276
                    ->normalizeKeys(false)
277
                    ->prototype('scalar')->end()
278
                ->end()
279
            ->end();
280
    }
281
282
    private function addImageMagickSection(ArrayNodeDefinition $rootNode)
283
    {
284
        $filtersInfo =
285
<<<EOT
286
DEPRECATED.
287
This is only used for legacy injection.
288
You may use imagick/gmagick liip_imagine bundle drivers.
289
290
Hash of filters to be used for your image variations config.
291
#   Key is the filter name, value is an argument passed to "convert" binary.
292
#   You can use numbered placeholders (aka input variables) that will be replaced by defined parameters in your image variations config
293
EOT;
294
295
        $rootNode
296
            ->children()
297
                ->arrayNode('imagemagick')
298
                    ->info('ImageMagick configuration')
299
                    ->children()
300
                        ->booleanNode('enabled')->defaultTrue()->end()
301
                        ->scalarNode('path')
302
                            ->info('Absolute path of ImageMagick / GraphicsMagick "convert" binary.')
303
                            ->beforeNormalization()
304
                                ->ifTrue(
305
                                    function ($v) {
306
                                        $basename = basename($v);
307
                                        // If there is a space in the basename, just drop it and everything after it.
308
                                        if (($wsPos = strpos($basename, ' ')) !== false) {
309
                                            $basename = substr($basename, 0, $wsPos);
310
                                        }
311
312
                                        return !is_executable(dirname($v) . DIRECTORY_SEPARATOR . $basename);
313
                                    }
314
                                )
315
                                ->thenInvalid('Please provide full path to ImageMagick / GraphicsMagick  "convert" binary. Please also check that it is executable.')
316
                            ->end()
317
                        ->end()
318
                        ->arrayNode('filters')
319
                            ->info($filtersInfo)
320
                            ->example(array('geometry/scaledownonly' => '"-geometry {1}x{2}>"'))
321
                            ->prototype('scalar')->end()
322
                        ->end()
323
                    ->end()
324
                ->end()
325
            ->end();
326
    }
327
328
    private function addHttpCacheSection(ArrayNodeDefinition $rootNode)
329
    {
330
        $purgeTypeInfo = <<<EOT
331
Http cache purge type.
332
333
Cache purge for content/locations is triggered when needed (e.g. on publish) and will result in one or several Http PURGE requests.
334
Can be "local" or "http" or a valid service ID:
335
- If "local" is used, an Http PURGE request will be emulated when needed (e.g. when using Symfony internal reverse proxy).
336
- If "http" is used, only one Http BAN request will be sent, with X-Location-Id header containing locationIds to ban.
337
  X-Location-Id consists in a Regexp containing locationIds to ban.
338
  Examples:
339
   - (123|456|789) => Purge locations #123, #456, #789.
340
   - .* => Purge all locations.
341
- If a serviceId is provided, it must be defined in the ServiceContainer and must implement eZ\Publish\Core\MVC\Symfony\Cache\PurgeClientInterface.
342
EOT;
343
344
        $rootNode
345
            ->children()
346
                ->arrayNode('http_cache')
347
                    ->children()
348
                        ->scalarNode('purge_type')
349
                            ->info($purgeTypeInfo)
350
                            ->defaultValue('local')
351
                            ->beforeNormalization()
352
                                ->ifTrue(
353
                                    function ($v) {
354
                                        $http = array('multiple_http' => true, 'single_http' => true);
355
356
                                        return isset($http[$v]);
357
                                    }
358
                                )
359
                                ->then(
360
                                    function () {
361
                                        return 'http';
362
                                    }
363
                                )
364
                            ->end()
365
                        ->end()
366
                        ->scalarNode('timeout')->info('DEPRECATED')->end()
367
                    ->end()
368
                ->end()
369
            ->end();
370
    }
371
372
    private function addPageSection(ArrayNodeDefinition $rootNode)
373
    {
374
        $pageInfo = <<<EOT
375
List of globally registered layouts and blocks used by the Page fieldtype
376
EOT;
377
378
        $rootNode
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 children() 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...
379
            ->children()
380
                ->arrayNode('ezpage')
381
                    ->info($pageInfo)
382
                    ->children()
383
                        ->arrayNode('layouts')
384
                            ->info('List of registered layouts, the key is the identifier of the layout')
385
                            ->useAttributeAsKey('key')
386
                            ->normalizeKeys(false)
387
                            ->prototype('array')
388
                                ->children()
389
                                    ->scalarNode('name')->isRequired()->info('Name of the layout')->end()
390
                                    ->scalarNode('template')->isRequired()->info('Template to use to render this layout')->end()
391
                                ->end()
392
                            ->end()
393
                        ->end()
394
                        ->arrayNode('blocks')
395
                            ->info('List of registered blocks, the key is the identifier of the block')
396
                            ->useAttributeAsKey('key')
397
                            ->normalizeKeys(false)
398
                            ->prototype('array')
399
                                ->children()
400
                                    ->scalarNode('name')->isRequired()->info('Name of the block')->end()
401
                                ->end()
402
                            ->end()
403
                        ->end()
404
                        ->arrayNode('enabledBlocks')
405
                            ->prototype('scalar')
406
                            ->end()
407
                            ->info('List of enabled blocks by default')
408
                        ->end()
409
                        ->arrayNode('enabledLayouts')
410
                            ->prototype('scalar')
411
                            ->end()
412
                            ->info('List of enabled layouts by default')
413
                        ->end()
414
                    ->end()
415
                ->end()
416
            ->end();
417
    }
418
419
    private function addRouterSection(ArrayNodeDefinition $rootNode)
420
    {
421
        $nonSAAwareInfo = <<<EOT
422
Route names that are not supposed to be SiteAccess aware, i.e. Routes pointing to asset generation (like assetic).
423
Note that you can just specify a prefix to match a selection of routes.
424
e.g. "_assetic_" will match "_assetic_*"
425
Defaults to ['_assetic_', '_wdt', '_profiler', '_configurator_']
426
EOT;
427
        $rootNode
428
            ->children()
429
                ->arrayNode('router')
430
                    ->children()
431
                        ->arrayNode('default_router')
432
                            ->children()
433
                                ->arrayNode('non_siteaccess_aware_routes')
434
                                    ->prototype('scalar')->end()
435
                                    ->info($nonSAAwareInfo)
436
                                    ->example(array('my_route_name', 'some_prefix_'))
437
                                ->end()
438
                            ->end()
439
                        ->end()
440
                    ->end()
441
                    ->info('Router related settings')
442
                ->end()
443
            ->end();
444
    }
445
}
446