Completed
Push — 3.0 ( 87f593...72eb21 )
by chihiro
24:34 queued 18s
created

Application::initSession()   B

Complexity

Conditions 7
Paths 24

Size

Total Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
nc 24
nop 0
dl 0
loc 45
rs 8.2666
c 0
b 0
f 0
1
<?php
2
/*
3
 * This file is part of EC-CUBE
4
 *
5
 * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
6
 *
7
 * http://www.ec-cube.co.jp/
8
 *
9
 * This program is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU General Public License
11
 * as published by the Free Software Foundation; either version 2
12
 * of the License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
 */
23
24
namespace Eccube;
25
26
use Binfo\Silex\MobileDetectServiceProvider;
27
use Eccube\Application\ApplicationTrait;
28
use Eccube\Common\Constant;
29
use Eccube\Doctrine\ORM\Mapping\Driver\YamlDriver;
30
use Eccube\EventListener\TransactionListener;
31
use Symfony\Component\EventDispatcher\EventDispatcher;
32
use Symfony\Component\Finder\Finder;
33
use Symfony\Component\HttpFoundation\Request;
34
use Symfony\Component\HttpFoundation\Response;
35
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
36
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
37
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
38
use Symfony\Component\HttpKernel\KernelEvents;
39
use Symfony\Component\Yaml\Yaml;
40
41
class Application extends ApplicationTrait
42
{
43
    protected static $instance;
44
45
    protected $initialized = false;
46
    protected $initializedPlugin = false;
47
    protected $testMode = false;
48
49
    public static function getInstance(array $values = array())
50
    {
51
        if (!is_object(self::$instance)) {
52
            self::$instance = new Application($values);
53
        }
54
55
        return self::$instance;
56
    }
57
58
    public static function clearInstance()
59
    {
60
        self::$instance = null;
61
    }
62
63
    final public function __clone()
64
    {
65
        throw new \Exception('Clone is not allowed against '.get_class($this));
66
    }
67
68
    public function __construct(array $values = array())
69
    {
70
        parent::__construct($values);
71
72
        if (is_null(self::$instance)) {
73
            self::$instance = $this;
74
        }
75
76
        // load config
77
        $this->initConfig();
78
79
        // init monolog
80
        $this->initLogger();
81
    }
82
83
    /**
84
     * Application::runが実行されているか親クラスのプロパティから判定
85
     *
86
     * @return bool
87
     */
88
    public function isBooted()
89
    {
90
        return $this->booted;
91
    }
92
93
    public function initConfig()
94
    {
95
        // load config
96
        $app = $this;
97
        $this['config'] = $this->share(function() use ($app) {
98
            $configAll = array();
99
            $app->parseConfig('constant', $configAll)
100
                ->parseConfig('path', $configAll)
101
                ->parseConfig('config', $configAll)
102
                ->parseConfig('database', $configAll)
103
                ->parseConfig('mail', $configAll)
104
                ->parseConfig('log', $configAll)
105
                ->parseConfig('nav', $configAll, true)
106
                ->parseConfig('doctrine_cache', $configAll)
107
                ->parseConfig('http_cache', $configAll)
108
                ->parseConfig('session_handler', $configAll);
109
110
            return $configAll;
111
        });
112
    }
113
114
    public function initLogger()
115
    {
116
        $app = $this;
117
        $this->register(new ServiceProvider\LogServiceProvider($app));
0 ignored issues
show
Unused Code introduced by
The call to LogServiceProvider::__construct() has too many arguments starting with $app.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
118
    }
119
120
    public function initialize()
121
    {
122
        if ($this->initialized) {
123
            return;
124
        }
125
126
        // init locale
127
        $this->initLocale();
128
129
        // init session
130
        if (!$this->isSessionStarted()) {
131
            $this->initSession();
132
        }
133
134
        // init twig
135
        $this->initRendering();
136
137
        // init provider
138
        $this->register(new \Silex\Provider\HttpCacheServiceProvider(), array(
139
            'http_cache.cache_dir' => __DIR__.'/../../app/cache/http/',
140
        ));
141
        $this->register(new \Silex\Provider\HttpFragmentServiceProvider());
142
        $this->register(new \Silex\Provider\UrlGeneratorServiceProvider());
143
        $this->register(new \Silex\Provider\FormServiceProvider());
144
        $this->register(new \Silex\Provider\SerializerServiceProvider());
145
        $this->register(new \Silex\Provider\ValidatorServiceProvider());
146
        $this->register(new MobileDetectServiceProvider());
147
148
        $app = $this;
149
        $this->error(function (\Exception $e, $code) use ($app) {
150
            if ($app['debug']) {
151
                return;
152
            }
153
154
            switch ($code) {
155
                case 403:
156
                    $title = 'アクセスできません。';
157
                    $message = 'お探しのページはアクセスができない状況にあるか、移動もしくは削除された可能性があります。';
158
                    break;
159
                case 404:
160
                    $title = 'ページがみつかりません。';
161
                    $message = 'URLに間違いがないかご確認ください。';
162
                    break;
163
                default:
164
                    $title = 'システムエラーが発生しました。';
165
                    $message = '大変お手数ですが、サイト管理者までご連絡ください。';
166
                    break;
167
            }
168
169
            return $app->render('error.twig', array(
170
                'error_title' => $title,
171
                'error_message' => $message,
172
            ));
173
        });
174
175
        // init mailer
176
        $this->initMailer();
177
178
        // init doctrine orm
179
        $this->initDoctrine();
180
181
        // Set up the DBAL connection now to check for a proper connection to the database.
182
        $this->checkDatabaseConnection();
183
184
        // init security
185
        $this->initSecurity();
186
187
        // init proxy
188
        $this->initProxy();
189
190
        // init ec-cube service provider
191
        $this->register(new ServiceProvider\EccubeServiceProvider());
192
193
        // mount controllers
194
        $this->register(new \Silex\Provider\ServiceControllerServiceProvider());
195
        $this->mount('', new ControllerProvider\FrontControllerProvider());
196
        $this->mount('/'.trim($this['config']['admin_route'], '/').'/', new ControllerProvider\AdminControllerProvider());
197
        Request::enableHttpMethodParameterOverride(); // PUTやDELETEできるようにする
198
199
        // add transaction listener
200
        $this['dispatcher']->addSubscriber(new TransactionListener($this));
201
202
        // init http cache
203
        $this->initCacheRequest();
204
205
        $this->initialized = true;
206
    }
207
208
    public function initLocale()
209
    {
210
211
        // timezone
212
        if (!empty($this['config']['timezone'])) {
213
            date_default_timezone_set($this['config']['timezone']);
214
        }
215
216
        $this->register(new \Silex\Provider\TranslationServiceProvider(), array(
217
            'locale' => $this['config']['locale'],
218
            'translator.cache_dir' => $this['debug'] ? null : $this['config']['root_dir'].'/app/cache/translator',
219
        ));
220
        $this['translator'] = $this->share($this->extend('translator', function ($translator, \Silex\Application $app) {
221
            $translator->addLoader('yaml', new \Symfony\Component\Translation\Loader\YamlFileLoader());
222
223
            $file = __DIR__.'/Resource/locale/validator.'.$app['locale'].'.yml';
224
            if (file_exists($file)) {
225
                $translator->addResource('yaml', $file, $app['locale'], 'validators');
226
            }
227
228
            $file = __DIR__.'/Resource/locale/message.'.$app['locale'].'.yml';
229
            if (file_exists($file)) {
230
                $translator->addResource('yaml', $file, $app['locale']);
231
            }
232
233
            return $translator;
234
        }));
235
    }
236
237
    public function initSession()
238
    {
239
        $root_urlpath = $this['config']['root_urlpath'] ?: '/';
240
        $ua = array_key_exists('HTTP_USER_AGENT', $_SERVER) ? $_SERVER['HTTP_USER_AGENT'] : '';
241
        $targetUaPatterns = array(
242
            '/^.*iPhone; CPU iPhone OS 1[0-2].*$/',
243
            '/^.*iPad; CPU OS 1[0-2].*$/',
244
            '/^.*iPod touch; CPU iPhone OS 1[0-2].*$/',
245
            '/^.*Macintosh; Intel Mac OS X.*Version\/1[0-2].*Safari.*$/',
246
        );
247
        $isUnsupported = array_filter($targetUaPatterns, function ($pattern) use ($ua) {
248
            return preg_match($pattern, $ua);
249
        });
250
        if ($this['config']['force_ssl'] == \Eccube\Common\Constant::ENABLED && !$isUnsupported) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $isUnsupported of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
251
            if (PHP_VERSION_ID >= 70300) {
252
                ini_set('session.cookie_path', $root_urlpath);
253
                ini_set('session.cookie_samesite', 'none');
254
            } else {
255
                ini_set('session.cookie_path', $root_urlpath.'; SameSite=none');
256
            }
257
        } else {
258
            ini_set('session.cookie_path', $root_urlpath);
259
        }
260
261
        $this->register(new \Silex\Provider\SessionServiceProvider(), array(
262
            'session.storage.save_path' => $this['config']['root_dir'].'/app/cache/eccube/session',
263
            'session.storage.options' => array(
264
                'name' => $this['config']['cookie_name'],
265
                'cookie_secure' => $this['config']['force_ssl'],
266
                'cookie_lifetime' => $this['config']['cookie_lifetime'],
267
                'cookie_httponly' => true,
268
                // cookie_domainは指定しない
269
                // http://blog.tokumaru.org/2011/10/cookiedomain.html
270
            ),
271
        ));
272
273
        $options = $this['config']['session_handler'];
274
275
        if ($options['enabled']) {
276
            // @see http://silex.sensiolabs.org/doc/providers/session.html#custom-session-configurations
277
            $this['session.storage.handler'] = null;
278
            ini_set('session.save_handler', $options['save_handler']);
279
            ini_set('session.save_path', $options['save_path']);
280
        }
281
    }
282
283
    public function initRendering()
284
    {
285
        $this->register(new \Silex\Provider\TwigServiceProvider(), array(
286
            'twig.form.templates' => array('Form/form_layout.twig'),
287
        ));
288
        $this['twig'] = $this->share($this->extend('twig', function (\Twig_Environment $twig, \Silex\Application $app) {
289
            $twig->addExtension(new \Eccube\Twig\Extension\EccubeExtension($app));
290
            $twig->addExtension(new \Twig_Extension_StringLoader());
291
292
            return $twig;
293
        }));
294
295
        $this->before(function (Request $request, \Silex\Application $app) {
296
            $app['admin'] = false;
297
            $app['front'] = false;
298
            $pathinfo = rawurldecode($request->getPathInfo());
299
            if (strpos($pathinfo, '/'.trim($app['config']['admin_route'], '/').'/') === 0) {
300
                $app['admin'] = true;
301
            } else {
302
                $app['front'] = true;
303
            }
304
305
            // フロント or 管理画面ごとにtwigの探索パスを切り替える.
306
            $app['twig'] = $app->share($app->extend('twig', function (\Twig_Environment $twig, \Silex\Application $app) {
307
                $paths = array();
308
309
                // 互換性がないのでprofiler とproduction 時のcacheを分離する
310
                if (isset($app['profiler'])) {
311
                    $cacheBaseDir = __DIR__.'/../../app/cache/twig/profiler/';
312
                } else {
313
                    $cacheBaseDir = __DIR__.'/../../app/cache/twig/production/';
314
                }
315
316
                if ($app->isAdminRequest()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Silex\Application as the method isAdminRequest() does only exist in the following sub-classes of Silex\Application: Eccube\Application, Eccube\Application\ApplicationTrait, Eccube\InstallApplication. 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...
317
                    if (file_exists(__DIR__.'/../../app/template/admin')) {
318
                        $paths[] = __DIR__.'/../../app/template/admin';
319
                    }
320
                    $paths[] = $app['config']['template_admin_realdir'];
321
                    $paths[] = __DIR__.'/../../app/Plugin';
322
                    $cache = $cacheBaseDir.'admin';
323
324
                } else {
325
                    if (file_exists($app['config']['template_realdir'])) {
326
                        $paths[] = $app['config']['template_realdir'];
327
                    }
328
                    $paths[] = $app['config']['template_default_realdir'];
329
                    $paths[] = __DIR__.'/../../app/Plugin';
330
                    $cache = $cacheBaseDir.$app['config']['template_code'];
331
                    $app['front'] = true;
332
                }
333
                $twig->setCache($cache);
334
                $app['twig.loader']->addLoader(new \Twig_Loader_Filesystem($paths));
335
336
                return $twig;
337
            }));
338
339
            // 管理画面のIP制限チェック.
340
            if ($app->isAdminRequest()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Silex\Application as the method isAdminRequest() does only exist in the following sub-classes of Silex\Application: Eccube\Application, Eccube\Application\ApplicationTrait, Eccube\InstallApplication. 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...
341
                // IP制限チェック
342
                $allowHost = $app['config']['admin_allow_host'];
343
                if (is_array($allowHost) && count($allowHost) > 0) {
344
                    if (array_search($app['request']->getClientIp(), $allowHost) === false) {
345
                        throw new \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException();
346
                    }
347
                }
348
            }
349
        }, self::EARLY_EVENT);
350
351
        // twigのグローバル変数を定義.
352
        $app = $this;
353
        $this->on(\Symfony\Component\HttpKernel\KernelEvents::CONTROLLER, function (\Symfony\Component\HttpKernel\Event\FilterControllerEvent $event) use ($app) {
354
            // 未ログイン時にマイページや管理画面以下にアクセスするとSubRequestで実行されるため,
355
            // $event->isMasterRequest()ではなく、グローバル変数が初期化済かどうかの判定を行う
356
            if (isset($app['twig_global_initialized']) && $app['twig_global_initialized'] === true) {
357
                return;
358
            }
359
            // ショップ基本情報
360
            $BaseInfo = $app['eccube.repository.base_info']->get();
361
            $app['twig']->addGlobal('BaseInfo', $BaseInfo);
362
363
            if ($app->isAdminRequest()) {
364
                // 管理画面
365
                // 管理画面メニュー
366
                $menus = array('', '', '');
367
                $app['twig']->addGlobal('menus', $menus);
368
369
                $Member = $app->user();
370
                if (is_object($Member)) {
371
                    // ログインしていれば管理者のロールを取得
372
                    $AuthorityRoles = $app['eccube.repository.authority_role']->findBy(array('Authority' => $Member->getAuthority()));
373
374
                    $roles = array();
375
                    foreach ($AuthorityRoles as $AuthorityRole) {
376
                        // 管理画面でメニュー制御するため相対パス全てをセット
377
                        $roles[] = $app['request']->getBaseUrl().'/'.$app['config']['admin_route'].$AuthorityRole->getDenyUrl();
378
                    }
379
380
                    $app['twig']->addGlobal('AuthorityRoles', $roles);
381
                }
382
383
            } else {
384
                // フロント画面
385
                $request = $event->getRequest();
386
                $route = $request->attributes->get('_route');
387
                $page = $route;
388
                // ユーザ作成画面
389
                if ($route === 'user_data') {
390
                    $params = $request->attributes->get('_route_params');
391
                    $route = $params['route'];
392
                    // プレビュー画面
393
                } elseif ($request->get('preview')) {
394
                    $route = 'preview';
395
                }
396
397
                try {
398
                    $DeviceType = $app['eccube.repository.master.device_type']
399
                        ->find(\Eccube\Entity\Master\DeviceType::DEVICE_TYPE_PC);
400
                    $PageLayout = $app['eccube.repository.page_layout']->getByUrl($DeviceType, $route, $page);
401
                } catch (\Doctrine\ORM\NoResultException $e) {
402
                    $PageLayout = $app['eccube.repository.page_layout']->newPageLayout($DeviceType);
403
                }
404
405
                $app['twig']->addGlobal('PageLayout', $PageLayout);
406
                $app['twig']->addGlobal('title', $PageLayout->getName());
407
            }
408
409
            $app['twig_global_initialized'] = true;
410
        });
411
    }
412
413
    public function initMailer()
414
    {
415
416
        // メール送信時の文字エンコード指定(デフォルトはUTF-8)
417
        if (isset($this['config']['mail']['charset_iso_2022_jp']) && is_bool($this['config']['mail']['charset_iso_2022_jp'])) {
418
            if ($this['config']['mail']['charset_iso_2022_jp'] === true) {
419
                \Swift::init(function () {
420
                    \Swift_DependencyContainer::getInstance()
421
                        ->register('mime.qpheaderencoder')
422
                        ->asAliasOf('mime.base64headerencoder');
423
                    \Swift_Preferences::getInstance()->setCharset('iso-2022-jp');
424
                });
425
            }
426
        }
427
428
        $this->register(new \Silex\Provider\SwiftmailerServiceProvider());
429
        $this['swiftmailer.options'] = $this['config']['mail'];
430
431
        if (isset($this['config']['mail']['use_spool']) && is_bool($this['config']['mail']['use_spool'])) {
432
            $this['swiftmailer.use_spool'] = $this['config']['mail']['use_spool'];
433
        }
434
        // デフォルトはsmtpを使用
435
        $transport = $this['config']['mail']['transport'];
436
        if ($transport == 'sendmail') {
437
            $this['swiftmailer.transport'] = \Swift_SendmailTransport::newInstance();
438
        } elseif ($transport == 'mail') {
439
            $this['swiftmailer.transport'] = \Swift_MailTransport::newInstance();
440
        }
441
    }
442
443
    public function initDoctrine()
444
    {
445
        $this->register(new \Silex\Provider\DoctrineServiceProvider(), array(
446
            'dbs.options' => array(
447
                'default' => $this['config']['database']
448
            )));
449
        $this->register(new \Saxulum\DoctrineOrmManagerRegistry\Silex\Provider\DoctrineOrmManagerRegistryProvider());
450
451
        // プラグインのmetadata定義を合わせて行う.
452
        $pluginConfigs = $this->getPluginConfigAll();
453
        $ormMappings = array();
454
        $ormMappings[] = array(
455
            'type' => 'yml',
456
            'namespace' => 'Eccube\Entity',
457
            'path' => array(
458
                __DIR__.'/Resource/doctrine',
459
                __DIR__.'/Resource/doctrine/master',
460
            ),
461
        );
462
463
        foreach ($pluginConfigs as $code) {
464
            $config = $code['config'];
465
            // Doctrine Extend
466
            if (isset($config['orm.path']) && is_array($config['orm.path'])) {
467
                $paths = array();
468
                foreach ($config['orm.path'] as $path) {
469
                    $paths[] = $this['config']['plugin_realdir'].'/'.$config['code'].$path;
470
                }
471
                $ormMappings[] = array(
472
                    'type' => 'yml',
473
                    'namespace' => 'Plugin\\'.$config['code'].'\\Entity',
474
                    'path' => $paths,
475
                );
476
            }
477
        }
478
479
        $options = array(
480
            'mappings' => $ormMappings
481
        );
482
483
        if (!$this['debug']) {
484
            $cacheDrivers = array();
485
            if (array_key_exists('doctrine_cache', $this['config'])) {
486
                $cacheDrivers = $this['config']['doctrine_cache'];
487
            }
488
489
            if (array_key_exists('metadata_cache', $cacheDrivers)) {
490
                $options['metadata_cache'] = $cacheDrivers['metadata_cache'];
491
            }
492
            if (array_key_exists('query_cache', $cacheDrivers)) {
493
                $options['query_cache'] = $cacheDrivers['query_cache'];
494
            }
495
            if (array_key_exists('result_cache', $cacheDrivers)) {
496
                $options['result_cache'] = $cacheDrivers['result_cache'];
497
            }
498
            if (array_key_exists('hydration_cache', $cacheDrivers)) {
499
                $options['hydration_cache'] = $cacheDrivers['hydration_cache'];
500
            }
501
        }
502
503
        $this->register(new \Dflydev\Silex\Provider\DoctrineOrm\DoctrineOrmServiceProvider(), array(
504
            'orm.proxies_dir' => __DIR__.'/../../app/cache/doctrine/proxies',
505
            'orm.em.options' => $options,
506
            'orm.custom.functions.string' => array(
507
                'NORMALIZE' => 'Eccube\Doctrine\ORM\Query\Normalize',
508
            ),
509
            'orm.custom.functions.numeric' => array(
510
                'EXTRACT' => 'Eccube\Doctrine\ORM\Query\Extract',
511
            ),
512
        ));
513
514
        /**
515
         * YamlDriverのPHP7対応. Doctrine2.4で修正されれば不要.
516
         * @see https://github.com/EC-CUBE/ec-cube/issues/1338
517
         */
518
        $config = $this['orm.em']->getConfiguration();
519
        /** @var $driver \Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain */
520
        $chain = $config->getMetadataDriverImpl();
521
        // $ormMappingsの1要素ごとにDriverが生成されている.
522
        $drivers = $chain->getDrivers();
523
        foreach ($drivers as $namespace => $oldDriver) {
524
            /** @var $newDriver \Eccube\Doctrine\ORM\Mapping\Driver\YamlDriver */
525
            $newDriver = new YamlDriver($oldDriver->getLocator());
526
            // 修正したDriverに差し替える. メソッド名はaddだけど実際はsetしてる.
527
            $chain->addDriver($newDriver, $namespace);
528
        }
529
    }
530
531
    public function initSecurity()
532
    {
533
        $this->register(new \Silex\Provider\SecurityServiceProvider());
534
        $this->register(new \Silex\Provider\RememberMeServiceProvider());
535
536
        $this['security.firewalls'] = array(
537
            'admin' => array(
538
                'pattern' => "^/{$this['config']['admin_route']}/",
539
                'form' => array(
540
                    'login_path' => "/{$this['config']['admin_route']}/login",
541
                    'check_path' => "/{$this['config']['admin_route']}/login_check",
542
                    'username_parameter' => 'login_id',
543
                    'password_parameter' => 'password',
544
                    'with_csrf' => true,
545
                    'use_forward' => true,
546
                    'default_target_path' => "/{$this['config']['admin_route']}",
547
                ),
548
                'logout' => array(
549
                    'logout_path' => "/{$this['config']['admin_route']}/logout",
550
                    'target_url' => "/{$this['config']['admin_route']}/",
551
                ),
552
                'users' => $this['orm.em']->getRepository('Eccube\Entity\Member'),
553
                'anonymous' => true,
554
            ),
555
            'customer' => array(
556
                'pattern' => '^/',
557
                'form' => array(
558
                    'login_path' => '/mypage/login',
559
                    'check_path' => '/login_check',
560
                    'username_parameter' => 'login_email',
561
                    'password_parameter' => 'login_pass',
562
                    'with_csrf' => true,
563
                    'use_forward' => true,
564
                ),
565
                'logout' => array(
566
                    'logout_path' => '/logout',
567
                    'target_url' => '/',
568
                ),
569
                'remember_me' => array(
570
                    'key' => sha1($this['config']['auth_magic']),
571
                    'name' => $this['config']['cookie_name'].'_rememberme',
572
                    // lifetimeはデフォルトの1年間にする
573
                    // 'lifetime' => $this['config']['cookie_lifetime'],
574
                    'path' => $this['config']['root_urlpath'] ?: '/',
575
                    'secure' => $this['config']['force_ssl'],
576
                    'httponly' => true,
577
                    'always_remember_me' => false,
578
                    'remember_me_parameter' => 'login_memory',
579
                ),
580
                'users' => $this['orm.em']->getRepository('Eccube\Entity\Customer'),
581
                'anonymous' => true,
582
            ),
583
        );
584
585
        $channel = null;
586
        // 強制SSL
587
        if ($this['config']['force_ssl'] == \Eccube\Common\Constant::ENABLED) {
588
            $channel = "https";
589
        }
590
591
        $this['security.access_rules'] = array(
592
            array("^/{$this['config']['admin_route']}/login", 'IS_AUTHENTICATED_ANONYMOUSLY', $channel),
593
            array("^/{$this['config']['admin_route']}/", 'ROLE_ADMIN', $channel),
594
            array('^/mypage/login', 'IS_AUTHENTICATED_ANONYMOUSLY', $channel),
595
            array('^/mypage/withdraw_complete', 'IS_AUTHENTICATED_ANONYMOUSLY', $channel),
596
            array('^/mypage/change', 'IS_AUTHENTICATED_FULLY', $channel),
597
            array('^/mypage', 'ROLE_USER', $channel),
598
        );
599
600
        $this['eccube.password_encoder'] = $this->share(function ($app) {
601
            return new \Eccube\Security\Core\Encoder\PasswordEncoder($app['config']);
602
        });
603
        $this['security.encoder_factory'] = $this->share(function ($app) {
604
            return new \Symfony\Component\Security\Core\Encoder\EncoderFactory(array(
605
                'Eccube\Entity\Customer' => $app['eccube.password_encoder'],
606
                'Eccube\Entity\Member' => $app['eccube.password_encoder'],
607
            ));
608
        });
609
        $this['eccube.event_listner.security'] = $this->share(function ($app) {
610
            return new \Eccube\EventListener\SecurityEventListener($app['orm.em']);
611
        });
612
        $this['user'] = function ($app) {
613
            $token = $app['security']->getToken();
614
615
            return ($token !== null) ? $token->getUser() : null;
616
        };
617
618
        // ログイン時のイベントを設定.
619
        $this['dispatcher']->addListener(\Symfony\Component\Security\Http\SecurityEvents::INTERACTIVE_LOGIN, array($this['eccube.event_listner.security'], 'onInteractiveLogin'));
620
621
        // Voterの設定
622
        $app = $this;
623
        $this['authority_voter'] = $this->share(function ($app) {
624
            return new \Eccube\Security\Voter\AuthorityVoter($app);
625
        });
626
627
        $app['security.voters'] = $app->extend('security.voters', function ($voters) use ($app) {
628
            $voters[] = $app['authority_voter'];
629
630
            return $voters;
631
        });
632
633
        $this['security.access_manager'] = $this->share(function ($app) {
634
            return new \Symfony\Component\Security\Core\Authorization\AccessDecisionManager($app['security.voters'], 'unanimous');
635
        });
636
637
        $app = $this;
638 View Code Duplication
        $app['security.authentication.success_handler.admin'] = $app->share(function ($app) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
639
            $handler = new \Eccube\Security\Http\Authentication\EccubeAuthenticationSuccessHandler(
640
                $app['security.http_utils'],
641
                $app['security.firewalls']['admin']['form']
642
            );
643
644
            $handler->setProviderKey('admin');
645
646
            return $handler;
647
        });
648
649 View Code Duplication
        $app['security.authentication.failure_handler.admin'] = $app->share(function ($app) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
650
            return new \Eccube\Security\Http\Authentication\EccubeAuthenticationFailureHandler(
651
                $app,
652
                $app['security.http_utils'],
653
                $app['security.firewalls']['admin']['form'],
654
                $app['logger']
655
            );
656
        });
657
658 View Code Duplication
        $app['security.authentication.success_handler.customer'] = $app->share(function ($app) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
659
            $handler = new \Eccube\Security\Http\Authentication\EccubeAuthenticationSuccessHandler(
660
                $app['security.http_utils'],
661
                $app['security.firewalls']['customer']['form']
662
            );
663
664
            $handler->setProviderKey('customer');
665
666
            return $handler;
667
        });
668
669 View Code Duplication
        $app['security.authentication.failure_handler.customer'] = $app->share(function ($app) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
670
            return new \Eccube\Security\Http\Authentication\EccubeAuthenticationFailureHandler(
671
                $app,
672
                $app['security.http_utils'],
673
                $app['security.firewalls']['customer']['form'],
674
                $app['logger']
675
            );
676
        });
677
    }
678
679
    /**
680
     * ロードバランサー、プロキシサーバの設定を行う
681
     */
682
    public function initProxy()
683
    {
684
        $config = $this['config'];
685
        if (isset($config['trusted_proxies_connection_only']) && !empty($config['trusted_proxies_connection_only'])) {
686
            $this->on(KernelEvents::REQUEST, function (GetResponseEvent $event) use ($config) {
687
                // サブリクエストのREMOTE_ADDRも動的に設定を行う必要があるため、KernelEvents::REQUESTを使用する
688
                Request::setTrustedProxies(array_merge(array($event->getRequest()->server->get('REMOTE_ADDR')), $config['trusted_proxies']));
689
            }, self::EARLY_EVENT);
690 View Code Duplication
        } elseif (isset($config['trusted_proxies']) && !empty($config['trusted_proxies'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
691
            Request::setTrustedProxies($config['trusted_proxies']);
692
        }
693
    }
694
695
    public function initializePlugin()
696
    {
697
        if ($this->initializedPlugin) {
698
            return;
699
        }
700
701
        // setup event dispatcher
702
        $this->initPluginEventDispatcher();
703
704
        // load plugin
705
        $this->loadPlugin();
706
707
        $this->initializedPlugin = true;
708
    }
709
710
    public function initPluginEventDispatcher()
711
    {
712
        // EventDispatcher
713
        $this['eccube.event.dispatcher'] = $this->share(function () {
714
            return new EventDispatcher();
715
        });
716
717
        $app = $this;
718
719
        // hook point
720
        $this->on(KernelEvents::REQUEST, function (GetResponseEvent $event) use ($app) {
721
            if (!$event->isMasterRequest()) {
722
                return;
723
            }
724
            $hookpoint = 'eccube.event.app.before';
725
            $app['eccube.event.dispatcher']->dispatch($hookpoint, $event);
726
        }, self::EARLY_EVENT);
727
728 View Code Duplication
        $this->on(KernelEvents::REQUEST, function (GetResponseEvent $event) use ($app) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
729
            if (!$event->isMasterRequest()) {
730
                return;
731
            }
732
            $route = $event->getRequest()->attributes->get('_route');
733
            $hookpoint = "eccube.event.controller.$route.before";
734
            $app['eccube.event.dispatcher']->dispatch($hookpoint, $event);
735
        });
736
737 View Code Duplication
        $this->on(KernelEvents::RESPONSE, function (FilterResponseEvent $event) use ($app) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
738
            if (!$event->isMasterRequest()) {
739
                return;
740
            }
741
            $route = $event->getRequest()->attributes->get('_route');
742
            $hookpoint = "eccube.event.controller.$route.after";
743
            $app['eccube.event.dispatcher']->dispatch($hookpoint, $event);
744
        });
745
746
        $this->on(KernelEvents::RESPONSE, function (FilterResponseEvent $event) use ($app) {
747
            if (!$event->isMasterRequest()) {
748
                return;
749
            }
750
            $hookpoint = 'eccube.event.app.after';
751
            $app['eccube.event.dispatcher']->dispatch($hookpoint, $event);
752
        }, self::LATE_EVENT);
753
754
        $this->on(KernelEvents::TERMINATE, function (PostResponseEvent $event) use ($app) {
755
            $route = $event->getRequest()->attributes->get('_route');
756
            $hookpoint = "eccube.event.controller.$route.finish";
757
            $app['eccube.event.dispatcher']->dispatch($hookpoint, $event);
758
        });
759
760
        $this->on(\Symfony\Component\HttpKernel\KernelEvents::RESPONSE, function (\Symfony\Component\HttpKernel\Event\FilterResponseEvent $event) use ($app) {
761
            if (!$event->isMasterRequest()) {
762
                return;
763
            }
764
            $route = $event->getRequest()->attributes->get('_route');
765
            $app['eccube.event.dispatcher']->dispatch('eccube.event.render.'.$route.'.before', $event);
766
        });
767
768
        // Request Event
769 View Code Duplication
        $this->on(\Symfony\Component\HttpKernel\KernelEvents::REQUEST, function (\Symfony\Component\HttpKernel\Event\GetResponseEvent $event) use ($app) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
770
771
            if (!$event->isMasterRequest()) {
772
                return;
773
            }
774
775
            $route = $event->getRequest()->attributes->get('_route');
776
777
            if (is_null($route)) {
778
                return;
779
            }
780
781
            $app['monolog']->debug('KernelEvents::REQUEST '.$route);
782
783
            // 全体
784
            $app['eccube.event.dispatcher']->dispatch('eccube.event.app.request', $event);
785
786
            if (strpos($route, 'admin') === 0) {
787
                // 管理画面
788
                $app['eccube.event.dispatcher']->dispatch('eccube.event.admin.request', $event);
789
            } else {
790
                // フロント画面
791
                $app['eccube.event.dispatcher']->dispatch('eccube.event.front.request', $event);
792
            }
793
794
            // ルーティング単位
795
            $app['eccube.event.dispatcher']->dispatch("eccube.event.route.{$route}.request", $event);
796
797
        }, 30); // Routing(32)が解決しし, 認証判定(8)が実行される前のタイミング.
798
799
        // Controller Event
800 View Code Duplication
        $this->on(\Symfony\Component\HttpKernel\KernelEvents::CONTROLLER, function (\Symfony\Component\HttpKernel\Event\FilterControllerEvent $event) use ($app) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
801
802
            if (!$event->isMasterRequest()) {
803
                return;
804
            }
805
806
            $route = $event->getRequest()->attributes->get('_route');
807
808
            if (is_null($route)) {
809
                return;
810
            }
811
812
            $app['monolog']->debug('KernelEvents::CONTROLLER '.$route);
813
814
            // 全体
815
            $app['eccube.event.dispatcher']->dispatch('eccube.event.app.controller', $event);
816
817
            if (strpos($route, 'admin') === 0) {
818
                // 管理画面
819
                $app['eccube.event.dispatcher']->dispatch('eccube.event.admin.controller', $event);
820
            } else {
821
                // フロント画面
822
                $app['eccube.event.dispatcher']->dispatch('eccube.event.front.controller', $event);
823
            }
824
825
            // ルーティング単位
826
            $app['eccube.event.dispatcher']->dispatch("eccube.event.route.{$route}.controller", $event);
827
        });
828
829
        // Response Event
830 View Code Duplication
        $this->on(\Symfony\Component\HttpKernel\KernelEvents::RESPONSE, function (\Symfony\Component\HttpKernel\Event\FilterResponseEvent $event) use ($app) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
831
            if (!$event->isMasterRequest()) {
832
                return;
833
            }
834
835
            $route = $event->getRequest()->attributes->get('_route');
836
837
            if (is_null($route)) {
838
                return;
839
            }
840
841
            $app['monolog']->debug('KernelEvents::RESPONSE '.$route);
842
843
            // ルーティング単位
844
            $app['eccube.event.dispatcher']->dispatch("eccube.event.route.{$route}.response", $event);
845
846
            if (strpos($route, 'admin') === 0) {
847
                // 管理画面
848
                $app['eccube.event.dispatcher']->dispatch('eccube.event.admin.response', $event);
849
            } else {
850
                // フロント画面
851
                $app['eccube.event.dispatcher']->dispatch('eccube.event.front.response', $event);
852
            }
853
854
            // 全体
855
            $app['eccube.event.dispatcher']->dispatch('eccube.event.app.response', $event);
856
        });
857
858
        // Exception Event
859 View Code Duplication
        $this->on(\Symfony\Component\HttpKernel\KernelEvents::EXCEPTION, function (\Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event) use ($app) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
860
861
            if (!$event->isMasterRequest()) {
862
                return;
863
            }
864
865
            $route = $event->getRequest()->attributes->get('_route');
866
867
            if (is_null($route)) {
868
                return;
869
            }
870
871
            $app['monolog']->debug('KernelEvents::EXCEPTION '.$route);
872
873
            // ルーティング単位
874
            $app['eccube.event.dispatcher']->dispatch("eccube.event.route.{$route}.exception", $event);
875
876
            if (strpos($route, 'admin') === 0) {
877
                // 管理画面
878
                $app['eccube.event.dispatcher']->dispatch('eccube.event.admin.exception', $event);
879
            } else {
880
                // フロント画面
881
                $app['eccube.event.dispatcher']->dispatch('eccube.event.front.exception', $event);
882
            }
883
884
            // 全体
885
            $app['eccube.event.dispatcher']->dispatch('eccube.event.app.exception', $event);
886
        });
887
888
        // Terminate Event
889 View Code Duplication
        $this->on(\Symfony\Component\HttpKernel\KernelEvents::TERMINATE, function (\Symfony\Component\HttpKernel\Event\PostResponseEvent $event) use ($app) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
890
891
            $route = $event->getRequest()->attributes->get('_route');
892
893
            if (is_null($route)) {
894
                return;
895
            }
896
897
            $app['monolog']->debug('KernelEvents::TERMINATE '.$route);
898
899
            // ルーティング単位
900
            $app['eccube.event.dispatcher']->dispatch("eccube.event.route.{$route}.terminate", $event);
901
902
            if (strpos($route, 'admin') === 0) {
903
                // 管理画面
904
                $app['eccube.event.dispatcher']->dispatch('eccube.event.admin.terminate', $event);
905
            } else {
906
                // フロント画面
907
                $app['eccube.event.dispatcher']->dispatch('eccube.event.front.terminate', $event);
908
            }
909
910
            // 全体
911
            $app['eccube.event.dispatcher']->dispatch('eccube.event.app.terminate', $event);
912
        });
913
    }
914
915
    public function loadPlugin()
916
    {
917
        // プラグインディレクトリを探索.
918
        $basePath = $this['config']['plugin_realdir'];
919
        $pluginConfigs = $this->getPluginConfigAll();
920
921
        // ハンドラ優先順位をdbから持ってきてハッシュテーブルを作成
922
        $priorities = array();
923
        $handlers = $this['orm.em']
924
            ->getRepository('Eccube\Entity\PluginEventHandler')
925
            ->getHandlers();
926
927
        foreach ($handlers as $handler) {
928
            if ($handler->getPlugin()->getEnable() && !$handler->getPlugin()->getDelFlg()) {
929
930
                $priority = $handler->getPriority();
931
            } else {
932
                // Pluginがdisable、削除済みの場合、EventHandlerのPriorityを全て0とみなす
933
                $priority = \Eccube\Entity\PluginEventHandler::EVENT_PRIORITY_DISABLED;
934
            }
935
            $priorities[$handler->getPlugin()->getClassName()][$handler->getEvent()][$handler->getHandler()] = $priority;
936
        }
937
938
        // プラグインをロードする.
939
        // config.yml/event.ymlの定義に沿ってインスタンスの生成を行い, イベント設定を行う.
940
        foreach ($pluginConfigs as $code => $pluginConfig) {
941
            // 正しい形式の pluginConfig のみ読み込む
942
            $path = $basePath.'/'.$code;
943
            try {
944
                $this['eccube.service.plugin']->checkPluginArchiveContent($path, $pluginConfig['config']);
945
            } catch (\Eccube\Exception\PluginException $e) {
946
                $this['monolog']->warning("Configuration file config.yml for plugin {$code} not found or is invalid. Skipping loading.", array(
947
                    'path' => $path,
948
                    'original-message' => $e->getMessage()
949
                ));
950
                continue;
951
            }
952
            $config = $pluginConfig['config'];
953
954
            $plugin = $this['orm.em']
955
                ->getRepository('Eccube\Entity\Plugin')
956
                ->findOneBy(array('code' => $config['code']));
957
958
            // const
959
            if (isset($config['const'])) {
960
                $this['config'] = $this->share($this->extend('config', function ($eccubeConfig) use ($config) {
961
                    $eccubeConfig[$config['code']] = array(
962
                        'const' => $config['const'],
963
                    );
964
965
                    return $eccubeConfig;
966
                }));
967
            }
968
969
            if ($plugin && $plugin->getEnable() == Constant::DISABLED) {
970
                // プラグインが無効化されていれば読み込まない
971
                continue;
972
            }
973
974
            // Type: Event
975
            if (isset($config['event'])) {
976
                $class = '\\Plugin\\'.$config['code'].'\\'.$config['event'];
977
                $eventExists = true;
978
979 View Code Duplication
                if (!class_exists($class)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
980
                    $this['monolog']->warning("Event class for plugin {$code} not exists.", array(
981
                        'class' => $class,
982
                    ));
983
                    $eventExists = false;
984
                }
985
986
                if ($eventExists && isset($config['event'])) {
987
988
                    $subscriber = new $class($this);
989
990
                    foreach ($pluginConfig['event'] as $event => $handlers) {
991
                        foreach ($handlers as $handler) {
992
                            if (!isset($priorities[$config['event']][$event][$handler[0]])) { // ハンドラテーブルに登録されていない(ソースにしか記述されていない)ハンドラは一番後ろにする
993
                                $priority = \Eccube\Entity\PluginEventHandler::EVENT_PRIORITY_LATEST;
994
                            } else {
995
                                $priority = $priorities[$config['event']][$event][$handler[0]];
996
                            }
997
                            // 優先度が0のプラグインは登録しない
998
                            if (\Eccube\Entity\PluginEventHandler::EVENT_PRIORITY_DISABLED != $priority) {
999
                                $this['eccube.event.dispatcher']->addListener($event, array($subscriber, $handler[0]), $priority);
1000
                            }
1001
                        }
1002
                    }
1003
                }
1004
            }
1005
            // Type: ServiceProvider
1006
            if (isset($config['service'])) {
1007
                foreach ($config['service'] as $service) {
1008
                    $class = '\\Plugin\\'.$config['code'].'\\ServiceProvider\\'.$service;
1009 View Code Duplication
                    if (!class_exists($class)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1010
                        $this['monolog']->warning("Service provider class for plugin {$code} not exists.", array(
1011
                            'class' => $class,
1012
                        ));
1013
                        continue;
1014
                    }
1015
                    $this->register(new $class($this));
1016
                }
1017
            }
1018
        }
1019
    }
1020
1021
    /**
1022
     * PHPUnit を実行中かどうかを設定する.
1023
     *
1024
     * @param boolean $testMode PHPUnit を実行中の場合 true
1025
     */
1026
    public function setTestMode($testMode)
1027
    {
1028
        $this->testMode = $testMode;
1029
    }
1030
1031
    /**
1032
     * PHPUnit を実行中かどうか.
1033
     *
1034
     * @return boolean PHPUnit を実行中の場合 true
1035
     */
1036
    public function isTestMode()
1037
    {
1038
        return $this->testMode;
1039
    }
1040
1041
    /**
1042
     *
1043
     * データベースの接続を確認
1044
     * 成功 : trueを返却
1045
     * 失敗 : \Doctrine\DBAL\DBALExceptionエラーが発生( 接続に失敗した場合 )、エラー画面を表示しdie()
1046
     * 備考 : app['debug']がtrueの際は処理を行わない
1047
     *
1048
     * @return boolean true
1049
     *
1050
     */
1051
    protected function checkDatabaseConnection()
1052
    {
1053
        if ($this['debug']) {
1054
            return;
1055
        }
1056
        try {
1057
            $this['db']->connect();
1058
        } catch (\Doctrine\DBAL\DBALException $e) {
1059
            $this['monolog']->error($e->getMessage());
1060
            $this['twig.path'] = array(__DIR__.'/Resource/template/exception');
1061
            $html = $this['twig']->render('error.twig', array(
1062
                'error_title' => 'データーベース接続エラー',
1063
                'error_message' => 'データーベースを確認してください',
1064
            ));
1065
            $response = new Response();
1066
            $response->setContent($html);
1067
            $response->setStatusCode('500');
1068
            $response->headers->set('Content-Type', 'text/html');
1069
            $response->send();
1070
            die();
1071
        }
1072
1073
        return true;
1074
    }
1075
1076
    /**
1077
     * Config ファイルをパースし、連想配列を返します.
1078
     *
1079
     * $config_name.yml ファイルをパースし、連想配列を返します.
1080
     * $config_name.php が存在する場合は、 PHP ファイルに記述された連想配列を使用します。
1081
     *
1082
     * @param string $config_name Config 名称
1083
     * @param array $configAll Config の連想配列
1084
     * @param boolean $wrap_key Config の連想配列に config_name のキーを生成する場合 true, デフォルト false
1085
     * @param string $ymlPath config yaml を格納したディレクトリ
1086
     * @param string $distPath config yaml dist を格納したディレクトリ
1087
     * @return Application
1088
     */
1089
    public function parseConfig($config_name, array &$configAll, $wrap_key = false, $ymlPath = null, $distPath = null)
1090
    {
1091
        $ymlPath = $ymlPath ? $ymlPath : __DIR__.'/../../app/config/eccube';
1092
        $distPath = $distPath ? $distPath : __DIR__.'/../../src/Eccube/Resource/config';
1093
        $config = array();
1094
        $config_php = $ymlPath.'/'.$config_name.'.php';
1095
        if (!file_exists($config_php)) {
1096
            $config_yml = $ymlPath.'/'.$config_name.'.yml';
1097
            if (file_exists($config_yml)) {
1098
                $config = Yaml::parse(file_get_contents($config_yml));
1099
                $config = empty($config) ? array() : $config;
1100 View Code Duplication
                if (isset($this['output_config_php']) && $this['output_config_php']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1101
                    file_put_contents($config_php, sprintf('<?php return %s', var_export($config, true)).';');
1102
                }
1103
            }
1104
        } else {
1105
            $config = require $config_php;
1106
        }
1107
1108
        $config_dist = array();
1109
        $config_php_dist = $distPath.'/'.$config_name.'.dist.php';
1110
        if (!file_exists($config_php_dist)) {
1111
            $config_yml_dist = $distPath.'/'.$config_name.'.yml.dist';
1112
            if (file_exists($config_yml_dist)) {
1113
                $config_dist = Yaml::parse(file_get_contents($config_yml_dist));
1114 View Code Duplication
                if (isset($this['output_config_php']) && $this['output_config_php']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1115
                    file_put_contents($config_php_dist, sprintf('<?php return %s', var_export($config_dist, true)).';');
1116
                }
1117
            }
1118
        } else {
1119
            $config_dist = require $config_php_dist;
1120
        }
1121
1122
        if ($wrap_key) {
1123
            $configAll = array_replace_recursive($configAll, array($config_name => $config_dist), array($config_name => $config));
1124
        } else {
1125
            $configAll = array_replace_recursive($configAll, $config_dist, $config);
1126
        }
1127
1128
        return $this;
1129
    }
1130
1131
    /**
1132
     * セッションが開始されているかどうか.
1133
     *
1134
     * @return boolean セッションが開始済みの場合 true
1135
     * @link http://php.net/manual/ja/function.session-status.php#113468
1136
     */
1137
    protected function isSessionStarted()
1138
    {
1139
        if (php_sapi_name() !== 'cli') {
1140
            if (version_compare(phpversion(), '5.4.0', '>=')) {
1141
                return session_status() === PHP_SESSION_ACTIVE ? true : false;
1142
            } else {
1143
                return session_id() === '' ? false : true;
1144
            }
1145
        }
1146
1147
        return false;
1148
    }
1149
1150
    /**
1151
     * Http Cache対応
1152
     */
1153
    protected function initCacheRequest()
1154
    {
1155
        // httpキャッシュが無効の場合はイベント設定を行わない.
1156
        if (!$this['config']['http_cache']['enabled']) {
1157
            return;
1158
        }
1159
1160
        $app = $this;
1161
1162
        // Response Event(http cache対応、event実行は一番遅く設定)
1163
        $this->on(\Symfony\Component\HttpKernel\KernelEvents::RESPONSE, function (\Symfony\Component\HttpKernel\Event\FilterResponseEvent $event) use ($app) {
1164
1165
            if (!$event->isMasterRequest()) {
1166
                return;
1167
            }
1168
1169
            $request = $event->getRequest();
1170
            $response = $event->getResponse();
1171
1172
            $route = $request->attributes->get('_route');
1173
1174
            $etag = md5($response->getContent());
1175
1176
            if (strpos($route, 'admin') === 0) {
1177
                // 管理画面
1178
1179
                // 管理画面ではコンテンツの中身が変更された時点でキャッシュを更新し、キャッシュの適用範囲はprivateに設定
1180
                $response->setCache(array(
1181
                    'etag' => $etag,
1182
                    'private' => true,
1183
                ));
1184
1185
                if ($response->isNotModified($request)) {
1186
                    return $response;
1187
                }
1188
1189
            } else {
1190
                // フロント画面
1191
                $cacheRoute = $app['config']['http_cache']['route'];
1192
1193
                if (in_array($route, $cacheRoute) === true) {
1194
                    // キャッシュ対象となる画面lが含まれていた場合、キャッシュ化
1195
                    // max-ageを設定しているためExpiresは不要
1196
                    // Last-Modifiedだと比較する項目がないためETagで対応
1197
                    // max-ageを設定していた場合、contentの中身が変更されても変更されない
1198
1199
                    $age = $app['config']['http_cache']['age'];
1200
1201
                    $response->setCache(array(
1202
                        'etag' => $etag,
1203
                        'max_age' => $age,
1204
                        's_maxage' => $age,
1205
                        'public' => true,
1206
                    ));
1207
1208
                    if ($response->isNotModified($request)) {
1209
                        return $response;
1210
                    }
1211
                }
1212
            }
1213
1214
        }, -1024);
1215
    }
1216
1217
    /**
1218
     * すべてのプラグインの設定情報を返す.
1219
     *
1220
     * すべてのプラグインの config.yml 及び event.yml を読み込み、連想配列で返す.
1221
     * キャッシュファイルが存在する場合は、キャッシュを利用する.
1222
     * キャッシュファイルが存在しない場合は、キャッシュを生成する.
1223
     * $app['debug'] = true の場合は、キャッシュを利用しない.
1224
     *
1225
     * @return array
1226
     */
1227
    public function getPluginConfigAll()
1228
    {
1229
        if ($this['debug']) {
1230
            return $this->parsePluginConfigs();
1231
        }
1232
        $pluginConfigCache = $this->getPluginConfigCacheFile();
1233
        if (file_exists($pluginConfigCache)) {
1234
            return require $pluginConfigCache;
1235
        }
1236
        if ($this->writePluginConfigCache($pluginConfigCache) === false) {
1237
            return $this->parsePluginConfigs();
1238
        } else {
1239
            return require $pluginConfigCache;
1240
        }
1241
    }
1242
1243
    /**
1244
     * プラグイン設定情報のキャッシュを書き込む.
1245
     *
1246
     * @param string $cacheFile
1247
     * @return int|boolean file_put_contents() の結果
1248
     */
1249
    public function writePluginConfigCache($cacheFile = null)
1250
    {
1251
        if (is_null($cacheFile)) {
1252
            $cacheFile = $this->getPluginConfigCacheFile();
1253
        }
1254
        $pluginConfigs = $this->parsePluginConfigs();
1255
        if (!file_exists($this['config']['plugin_temp_realdir'])) {
1256
            @mkdir($this['config']['plugin_temp_realdir']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1257
        }
1258
        $this['monolog']->debug("write plugin config cache", array($pluginConfigs));
1259
        return file_put_contents($cacheFile, sprintf('<?php return %s', var_export($pluginConfigs, true)).';');
1260
    }
1261
1262
    /**
1263
     * プラグイン設定情報のキャッシュファイルを削除する.
1264
     *
1265
     * @return boolean
1266
     */
1267
    public function removePluginConfigCache()
1268
    {
1269
        $cacheFile = $this->getPluginConfigCacheFile();
1270
        if (file_exists($cacheFile)) {
1271
            $this['monolog']->debug("remove plugin config cache");
1272
            return unlink($cacheFile);
1273
        }
1274
        return false;
1275
    }
1276
1277
    /**
1278
     * プラグイン設定情報のキャッシュファイルパスを返す.
1279
     *
1280
     * @return string
1281
     */
1282
    public function getPluginConfigCacheFile()
1283
    {
1284
        return $this['config']['plugin_temp_realdir'].'/config_cache.php';
1285
    }
1286
1287
    /**
1288
     * プラグイン設定情報をパースし, 連想配列で返す.
1289
     *
1290
     * すべてのプラグインを探索し、 config.yml 及び event.yml をパースする.
1291
     * パースした情報を連想配列で返す.
1292
     *
1293
     * @return array
1294
     */
1295
    public function parsePluginConfigs()
1296
    {
1297
1298
        $finder = Finder::create()
1299
            ->in($this['config']['plugin_realdir'])
1300
            ->directories()
1301
            ->depth(0);
1302
        $finder->sortByName();
1303
1304
        $pluginConfigs = array();
1305
        foreach ($finder as $dir) {
1306
            $code = $dir->getBaseName();
1307
            if (!$code) {
1308
                //PHP5.3のgetBaseNameバグ対応
1309
                if (PHP_VERSION_ID < 50400) {
1310
                    $code = $dir->getFilename();
1311
                }
1312
            }
1313
            $file = $dir->getRealPath().'/config.yml';
1314
            $config = null;
0 ignored issues
show
Unused Code introduced by
$config is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1315 View Code Duplication
            if (file_exists($file)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1316
                $config = Yaml::parse(file_get_contents($file));
1317
            } else {
1318
                $this['monolog']->warning("skip {$code} orm.path loading. config.yml not found.", array('path' => $file));
1319
                continue;
1320
            }
1321
1322
            $file = $dir->getRealPath().'/event.yml';
1323
            $event = null;
1324 View Code Duplication
            if (file_exists($file)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1325
                $event = Yaml::parse(file_get_contents($file));
1326
            } else {
1327
                $this['monolog']->info("skip {$code} event.yml not found.", array('path' => $file));
1328
            }
1329
            if (!is_null($config)) {
1330
                $pluginConfigs[$code] = array(
1331
                    'config' => $config,
1332
                    'event' => $event
1333
                );
1334
                $this['monolog']->debug("parse {$code} config", array($code => $pluginConfigs[$code]));
1335
            }
1336
        }
1337
1338
        return $pluginConfigs;
1339
    }
1340
}
1341