Completed
Push — 7.0 ( 0ab566...ac6efe )
by André
20:30
created

Configuration   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 445
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
dl 0
loc 445
rs 10
c 0
b 0
f 0
wmc 24
lcom 1
cbo 8

9 Methods

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