Failed Conditions
Pull Request — experimental/3.1 (#2299)
by chihiro
40:22
created

src/Eccube/Application.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/*
3
 * This file is part of EC-CUBE
4
 *
5
 * Copyright(c) 2000-2015 LOCKON CO.,LTD. All Rights Reserved.
6
 *
7
 * http://www.lockon.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 Doctrine\Common\Annotations\AnnotationReader;
27
use Doctrine\Common\Annotations\CachedReader;
28
use Doctrine\Common\Cache\ArrayCache;
29
use Doctrine\DBAL\Types\Type;
30
use Eccube\Application\ApplicationTrait;
31
use Eccube\Common\Constant;
32
use Eccube\Doctrine\DBAL\Types\UTCDateTimeType;
33
use Eccube\Doctrine\ORM\Mapping\Driver\AnnotationDriver;
34
use Eccube\Doctrine\ORM\Mapping\Driver\YamlDriver;
35
use Eccube\EventListener\TransactionListener;
36
use Eccube\Plugin\ConfigManager as PluginConfigManager;
37
use Eccube\Routing\EccubeRouter;
38
use Eccube\ServiceProvider\EntityEventServiceProvider;
39
use Eccube\ServiceProvider\MobileDetectServiceProvider;
40
use Sergiors\Silex\Provider\AnnotationsServiceProvider;
41
use Sergiors\Silex\Provider\DoctrineCacheServiceProvider;
42
use Sergiors\Silex\Provider\RoutingServiceProvider;
43
use Sergiors\Silex\Provider\SensioFrameworkExtraServiceProvider;
44
use Sergiors\Silex\Provider\TemplatingServiceProvider;
45
use Sergiors\Silex\Routing\ChainUrlGenerator;
46
use Sergiors\Silex\Routing\ChainUrlMatcher;
47
use Symfony\Component\EventDispatcher\EventDispatcher;
48
use Symfony\Component\Finder\Finder;
49
use Symfony\Component\HttpFoundation\Request;
50
use Symfony\Component\HttpFoundation\Response;
51
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
52
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
53
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
54
use Symfony\Component\HttpKernel\KernelEvents;
55
use Symfony\Component\Yaml\Yaml;
56
57
class Application extends \Silex\Application
58
{
59
    use \Silex\Application\FormTrait;
60
    use \Silex\Application\UrlGeneratorTrait;
61
    use \Silex\Application\MonologTrait;
62
    use \Silex\Application\SwiftmailerTrait;
63
    use \Silex\Application\SecurityTrait;
64
    use \Silex\Application\TranslationTrait;
65
    use \Eccube\Application\ApplicationTrait;
66
    use \Eccube\Application\SecurityTrait;
67 1179
    use \Eccube\Application\TwigTrait;
68
69 1179
    protected static $instance;
70 1178
71
    protected $initialized = false;
72
    protected $initializedPlugin = false;
73 1179
    protected $testMode = false;
74
75
    public static function getInstance(array $values = array())
76 1179
    {
77
        if (!is_object(self::$instance)) {
78 1179
            self::$instance = new Application($values);
79
        }
80
81
        return self::$instance;
82
    }
83
84
    public static function clearInstance()
85
    {
86 1179
        self::$instance = null;
87
    }
88 1179
89
    final public function __clone()
90 1179
    {
91 1179
        throw new \Exception('Clone is not allowed against '.get_class($this));
92
    }
93
94
    public function __construct(array $values = array())
95 1179
    {
96
        parent::__construct($values);
97
98 1179
        if (is_null(self::$instance)) {
99
            self::$instance = $this;
100
        }
101
102
        // load config
103
        $this->initConfig();
104
105
        // init monolog
106 1187
        $this->initLogger();
107
    }
108 1187
109
    /**
110
     * Application::runが実行されているか親クラスのプロパティから判定
111 1179
     *
112
     * @return bool
113
     */
114
    public function isBooted()
115 1179
    {
116 1179
        return $this->booted;
117 1179
    }
118 1179
119 1179
    public function initConfig()
120 1179
    {
121 1179
        // load config
122 1179
        $this['config'] = function() {
123 1179
            $configAll = array();
124 1179
            $this->parseConfig('constant', $configAll)
125 1179
                ->parseConfig('path', $configAll)
126
                ->parseConfig('config', $configAll)
127 1179
                ->parseConfig('database', $configAll)
128
                ->parseConfig('mail', $configAll)
129
                ->parseConfig('log', $configAll)
130
                ->parseConfig('nav', $configAll, true)
131 1179
                ->parseConfig('doctrine_cache', $configAll)
132
                ->parseConfig('http_cache', $configAll)
133 1179
                ->parseConfig('session_handler', $configAll);
134 1179
135
            return $configAll;
136
        };
137 1179
    }
138
139 1179
    public function initLogger()
140 4
    {
141
        $app = $this;
142
        $this->register(new ServiceProvider\LogServiceProvider($app));
143
    }
144 1179
145
    public function initialize()
146
    {
147 1179
        if ($this->initialized) {
148 1179
            return;
149
        }
150
151
        // init locale
152 1179
        $this->initLocale();
153
154
        // init session
155 1179
        if (!$this->isSessionStarted()) {
156 1179
            $this->initSession();
157
        }
158 1179
159 1179
        // init twig
160 1179
        $this->initRendering();
161 1179
162
        // init provider
163
        $this->register(new \Silex\Provider\HttpCacheServiceProvider(), array(
164 33
            'http_cache.cache_dir' => __DIR__.'/../../app/cache/http/',
165 33
        ));
166
        $this->register(new \Silex\Provider\HttpFragmentServiceProvider());
167
        $this->register(new \Silex\Provider\FormServiceProvider());
168
        $this->register(new \Silex\Provider\SerializerServiceProvider());
169
        $this->register(new \Silex\Provider\ValidatorServiceProvider());
170
        $this->register(new \Saxulum\Validator\Provider\SaxulumValidatorProvider());
171
        $this->register(new MobileDetectServiceProvider());
172
173
        $this->error(function (\Exception $e, Request $request, $code) {
174
            if ($this['debug']) {
175
                return;
176
            }
177
178
            switch ($code) {
179
                case 403:
180
                    $title = 'アクセスできません。';
181
                    $message = 'お探しのページはアクセスができない状況にあるか、移動もしくは削除された可能性があります。';
182
                    break;
183
                case 404:
184
                    $title = 'ページがみつかりません。';
185
                    $message = 'URLに間違いがないかご確認ください。';
186
                    break;
187 1179
                default:
188
                    $title = 'システムエラーが発生しました。';
189
                    $message = '大変お手数ですが、サイト管理者までご連絡ください。';
190 1179
                    break;
191
            }
192
193 1179
            return $this->render('error.twig', array(
194
                'error_title' => $title,
195
                'error_message' => $message,
196 1179
            ));
197
        });
198
199 1179
        // init mailer
200
        $this->initMailer();
201 1179
202 1179
        // init doctrine orm
203 1179
        $this->initDoctrine();
204 1179
205 1179
        // Set up the DBAL connection now to check for a proper connection to the database.
206 1179
        $this->checkDatabaseConnection();
207 1179
208
        // init security
209 1179
        $this->initSecurity();
210 1179
211
        $this->register(new \Sergiors\Silex\Provider\RoutingServiceProvider(), [
212
            'routing.cache_dir' => $this['debug'] ? null : __DIR__.'/../../app/cache/routing'
213 1179
        ]);
214
        $this->register(new \Sergiors\Silex\Provider\DoctrineCacheServiceProvider());
215
        $this->register(new \Sergiors\Silex\Provider\TemplatingServiceProvider());
216
        $this->register(new \Sergiors\Silex\Provider\AnnotationsServiceProvider(), [
217 1179
            'annotations.debug' => $this['debug'],
218
            'annotations.options' => [
219
                'cache_driver' => $this['debug'] ? 'array' : 'filesystem',
220 1179
                'cache_dir' => $this['debug'] ? null : __DIR__.'/../../app/cache/annotation'
221
            ]
222
        ]);
223 1179
        $this->register(new \Sergiors\Silex\Provider\SensioFrameworkExtraServiceProvider(), [
224 1179
            'request' => [
225 1179
                'auto_convert' => true
226 1179
            ]
227
        ]);
228
        // init proxy
229
        $this->initProxy();
230 1179
231
        // init ec-cube service provider
232
        $this->register(new ServiceProvider\EccubeServiceProvider());
233 1178
234 1178
        // mount controllers
235 1178
        $this->register(new \Silex\Provider\ServiceControllerServiceProvider());
236 1178
        $this->mount('', new ControllerProvider\FrontControllerProvider());
237 1178
        $this->mount('/'.trim($this['config']['admin_route'], '/').'/', new ControllerProvider\AdminControllerProvider());
238 1178
        Request::enableHttpMethodParameterOverride(); // PUTやDELETEできるようにする
239
240 1178
        // ルーティングの設定
241 1178
        // TODO EccubeRoutingServiceProviderに移植する.
242
        $app = $this;
243
        $this['eccube.router'] = $this->protect(function($resoure, $cachePrefix) use ($app) {
244 1178
            $options = [
245 1178
                'debug' => $app['debug'],
246
                'cache_dir' => $app['routing.cache_dir'],
247
                'matcher_base_class' => $app['request_matcher_class'],
248 1178
                'matcher_class' => $app['request_matcher_class'],
249 1178
                'matcher_cache_class' => $cachePrefix.'UrlMatcher',
250 1178
                'generator_cache_class' => $cachePrefix.'UrlGenerator'
251
            ];
252 1178
            $router = new EccubeRouter(
253 1179
                $app['routing.loader'],
254
                $resoure,
255
                $options,
256 1178
                $app['request_context'],
257 1178
                $app['logger']
258
            );
259 1178
260
            $router->setAdminPrefix($app['config']['admin_route']);
261
            $router->setUserDataPrefix($app['config']['user_data_route']);
262
            $router->setRequireHttps($app['config']['force_ssl']);
263
264 1178
            return $router;
265 1178
        });
266 1178
267 1178
        $this['eccube.router.origin'] = function ($app) {
268
            $resource = __DIR__.'/Controller';
269 1178
            $cachePrefix = 'Origin';
270 1178
271 1178
            return $app['eccube.router']($resource, $cachePrefix);
272 1178
        };
273 1178
274
        $this['eccube.routers.plugin'] = function ($app) {
275
            // TODO 有効なプラグインを対象とする必要がある.
276 1178
            $dirs = Finder::create()
277
                ->in($app['config']['root_dir'].'/app/Plugin')
278
                ->name('Controller')
279
                ->directories();
280
281 1178
            $routers = [];
282 1178
            foreach ($dirs as $dir) {
283
                $realPath = $dir->getRealPath();
284 1178
                $pluginCode = basename(dirname($realPath));
285
                $routers[] = $app['eccube.router']($realPath, 'Plugin'.$pluginCode);
286 1178
            }
287
288
            return $routers;
289
        };
290 1178
291 1178
        $this['eccube.router.extend'] = function ($app) {
292 1178
            // TODO ディレクトリ名は暫定
293 1178
            $resource = $app['config']['root_dir'].'/app/Acme/Controller';
294
            $cachePrefix = 'Extend';
295 1178
296 1178
            $router = $app['eccube.router']($resource, $cachePrefix);
297
298 1178
            return $router;
299 1179
        };
300
301 View Code Duplication
        $this->extend('request_matcher', function ($matcher, $app) {
302 1178
            $matchers = [];
303 1178
            $matchers[] = $app['eccube.router.extend'];
304 1178
            foreach ($app['eccube.routers.plugin'] as $router) {
305 1178
                $matchers[] = $router;
306
            };
307 1178
            $matchers[] = $app['eccube.router.origin'];
308 1178
            $matchers[] = $matcher;
309
310 1178
            return new ChainUrlMatcher($matchers, $app['request_context']);
311 1179
        });
312
313 View Code Duplication
        $this->extend('url_generator', function ($generator, $app) {
314 1179
            $generators = [];
315
            $generators[] = $app['eccube.router.extend'];
316 1179
            foreach ($app['eccube.routers.plugin'] as $router) {
317
                $generators[] = $router;
318
            };
319 1179
            $generators[] = $app['eccube.router.origin'];
320
            $generators[] = $generator;
321
322
            return new ChainUrlGenerator($generators, $app['request_context']);
323 1179
        });
324 1179
325
        // init http cache
326
        $this->initCacheRequest();
327 1179
328 1179
        $this->initialized = true;
329 1179
    }
330
331
    public function initLocale()
332
    {
333 1178
334
        // timezone
335 1178
        if (!empty($this['config']['timezone'])) {
336 1178
            date_default_timezone_set($this['config']['timezone']);
337 1178
        }
338
339
        $this->register(new \Silex\Provider\TranslationServiceProvider(), array(
340 1178
            'locale' => $this['config']['locale'],
341 1178
            'translator.cache_dir' => $this['debug'] ? null : $this['config']['root_dir'].'/app/cache/translator',
342 1178
            'locale_fallbacks' => ['ja', 'en'],
343
        ));
344
        $this->extend('translator', function ($translator, \Silex\Application $app) {
345 1178
            $translator->addLoader('yaml', new \Symfony\Component\Translation\Loader\YamlFileLoader());
346 1179
347
            $file = __DIR__.'/Resource/locale/validator.'.$app['locale'].'.yml';
348
            if (file_exists($file)) {
349 1179
                $translator->addResource('yaml', $file, $app['locale'], 'validators');
350
            }
351 1179
352 1179
            $file = __DIR__.'/Resource/locale/message.'.$app['locale'].'.yml';
353
            if (file_exists($file)) {
354 1179
                $translator->addResource('yaml', $file, $app['locale']);
355 1179
            }
356 1179
357 1179
            return $translator;
358
        });
359
    }
360
361
    public function initSession()
362
    {
363
        $this->register(new \Silex\Provider\SessionServiceProvider(), array(
364 1179
            'session.storage.save_path' => $this['config']['root_dir'].'/app/cache/eccube/session',
365
            'session.storage.options' => array(
366 1179
                'name' => $this['config']['cookie_name'],
367
                'cookie_path' => $this['config']['root_urlpath'] ?: '/',
368
                'cookie_secure' => $this['config']['force_ssl'],
369
                'cookie_lifetime' => $this['config']['cookie_lifetime'],
370
                'cookie_httponly' => true,
371
                // cookie_domainは指定しない
372
                // http://blog.tokumaru.org/2011/10/cookiedomain.html
373
            ),
374 1179
        ));
375
376 1179
        $options = $this['config']['session_handler'];
377 1179
378
        if ($options['enabled']) {
379
            // @see http://silex.sensiolabs.org/doc/providers/session.html#custom-session-configurations
380 1178
            $this['session.storage.handler'] = null;
381 1178
            ini_set('session.save_handler', $options['save_handler']);
382
            ini_set('session.save_path', $options['save_path']);
383 1178
        }
384 1179
    }
385
386
    public function initRendering()
387 475
    {
388 475
        $this->register(new \Silex\Provider\TwigServiceProvider(), array(
389 475
            'twig.form.templates' => array('Form/form_layout.twig'),
390 475
        ));
391 300
        $this->extend('twig', function (\Twig_Environment $twig, \Silex\Application $app) {
392
            $twig->addExtension(new \Eccube\Twig\Extension\EccubeExtension($app));
393 177
            $twig->addExtension(new \Twig_Extension_StringLoader());
394
395 475
            return $twig;
396 300
        });
397 300
398
        $this->before(function (Request $request, \Silex\Application $app) {
399 300
            $app['admin'] = $app['front'] = false;
400 300
            $pathinfo = rawurldecode($request->getPathInfo());
401
            if (strpos($pathinfo, '/'.trim($app['config']['admin_route'], '/').'/') === 0) {
402
                $app['admin'] = true;
403
            } else {
404 177
                $app['front'] = true;
405 177
            }
406
407 177
            // フロント or 管理画面ごとにtwigの探索パスを切り替える.
408 177
            if ($app->isAdminRequest()) {
409
                if (file_exists(__DIR__.'/../../app/template/admin')) {
410 177
                    $paths[] = __DIR__.'/../../app/template/admin';
411
                }
412
                $paths[] = $app['config']['template_admin_realdir'];
413 475
                $paths[] = __DIR__.'/../../app/Plugin';
414
                $cacheDir =  __DIR__.'/../../app/cache/twig/admin';
415
            } else {
416 475
                // モバイル端末時、smartphoneディレクトリを探索パスに追加する.
417
                if ($app['mobile_detect.device_type'] == \Eccube\Entity\Master\DeviceType::DEVICE_TYPE_SP) {
418
                    if (file_exists(__DIR__.'/../../app/template/smartphone')) {
419
                        $paths[] = __DIR__.'/../../app/template/smartphone';
420
                    }
421
                    $paths[] = __DIR__.'/Resource/template/smartphone';
422
                }
423
424
                if (file_exists($app['config']['template_realdir'])) {
425
                    $paths[] = $app['config']['template_realdir'];
426
                }
427
                $paths[] = $app['config']['template_default_realdir'];
428
                $paths[] = __DIR__.'/../../app/Plugin';
429
                $cacheDir =  __DIR__.'/../../app/cache/twig/'.$app['config']['template_code'];
430
            }
431
            $app['twig']->setCache($app['debug'] ? null : $cacheDir);
432
            $app['twig.loader']->addLoader(new \Twig_Loader_Filesystem($paths));
433
434
            // 管理画面のIP制限チェック.
435
            if ($app->isAdminRequest()) {
436
                // IP制限チェック
437
                $allowHost = $app['config']['admin_allow_host'];
438
                if (count($allowHost) > 0) {
439
                    if (array_search($app['request_stack']->getCurrentRequest()->getClientIp(), $allowHost) === false) {
440
                        throw new \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException();
441
                    }
442
                }
443
            }
444
        }, self::EARLY_EVENT);
445
446
        // twigのグローバル変数を定義.
447
        $this->on(\Symfony\Component\HttpKernel\KernelEvents::CONTROLLER, function (\Symfony\Component\HttpKernel\Event\FilterControllerEvent $event) {
448
            // 未ログイン時にマイページや管理画面以下にアクセスするとSubRequestで実行されるため,
449
            // $event->isMasterRequest()ではなく、グローバル変数が初期化済かどうかの判定を行う
450
            if (isset($this['twig_global_initialized']) && $this['twig_global_initialized'] === true) {
451
                return;
452
            }
453 475
            // ショップ基本情報
454
            $BaseInfo = $this['eccube.repository.base_info']->get();
455 300
            $this['twig']->addGlobal('BaseInfo', $BaseInfo);
456 300
457
            if ($this->isAdminRequest()) {
458
                // 管理画面
459
                // 管理画面メニュー
460
                $menus = array('', '', '');
461
                $this['twig']->addGlobal('menus', $menus);
462 1179
463
                $Member = $this->user();
464
                if (is_object($Member)) {
465
                    // ログインしていれば管理者のロールを取得
466
                    $AuthorityRoles = $this['eccube.repository.authority_role']->findBy(array('Authority' => $Member->getAuthority()));
467
468 470
                    $roles = array();
469 122
                    $request = $event->getRequest();
470
                    foreach ($AuthorityRoles as $AuthorityRole) {
471
                        // 管理画面でメニュー制御するため相対パス全てをセット
472 470
                        $roles[] = $request->getBaseUrl().'/'.$this['config']['admin_route'].$AuthorityRole->getDenyUrl();
473 470
                    }
474
475 470
                    $this['twig']->addGlobal('AuthorityRoles', $roles);
476
                }
477
478 300
            } else {
479 300
                // フロント画面
480
                $request = $event->getRequest();
481 300
                $route = $request->attributes->get('_route');
482 300
483
                // ユーザ作成画面
484 294
                if ($route === 'user_data') {
485
                    $params = $request->attributes->get('_route_params');
486 294
                    $route = $params['route'];
487 294
                    // プレビュー画面
488 294
                } elseif ($request->get('preview')) {
489
                    $route = 'preview';
490 294
                }
491
492
                try {
493 300
                    $device_type_id = $this['mobile_detect.device_type'];
494
495
                    // TODO デバッグ用
496
                    if ($request->query->has('device_type_id')) {
497
                        $device_type_id = $request->get('device_type_id', \Eccube\Entity\Master\DeviceType::DEVICE_TYPE_PC);
498 170
                    }
499 170
500
                    $DeviceType = $this['eccube.repository.master.device_type']
501
                        ->find($device_type_id);
502 170
                    $qb = $this['eccube.repository.page_layout']->createQueryBuilder('p');
503 2
                    $PageLayout = $qb->select('p, pll,l, bp, b')
504 2
                        ->leftJoin('p.PageLayoutLayouts', 'pll')
505
                        ->leftJoin('pll.Layout', 'l')
506 168
                        ->leftJoin('l.BlockPositions', 'bp')
507
                        ->leftJoin('bp.Block', 'b')
508
                        ->where('p.url = :route')
509
                        ->andWhere('l.DeviceType = :DeviceType')
510
                        ->orderBy('bp.block_row', 'ASC')
511 170
                        ->setParameter('route', $route)
512 170
                        ->setParameter('DeviceType', $DeviceType)
513 170
                        ->getQuery()
514 69
                        ->getSingleResult();
515 69
                } catch (\Doctrine\ORM\NoResultException $e) {
516
                    $PageLayout = $this['eccube.repository.page_layout']->newPageLayout($DeviceType);
0 ignored issues
show
The variable $DeviceType does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
517
                }
518 170
519 170
                $this['twig']->addGlobal('PageLayout', $PageLayout);
520
                $this['twig']->addGlobal('title', $PageLayout->getName());
521
            }
522 470
523 1179
            $this['twig_global_initialized'] = true;
524
        });
525
    }
526 1179
527
    public function initMailer()
528
    {
529
530 1179
        // メール送信時の文字エンコード指定(デフォルトはUTF-8)
531 1179
        if (isset($this['config']['mail']['charset_iso_2022_jp']) && is_bool($this['config']['mail']['charset_iso_2022_jp'])) {
532
            if ($this['config']['mail']['charset_iso_2022_jp'] === true) {
533
                \Swift::init(function () {
534
                    \Swift_DependencyContainer::getInstance()
535
                        ->register('mime.qpheaderencoder')
536
                        ->asAliasOf('mime.base64headerencoder');
537
                    \Swift_Preferences::getInstance()->setCharset('iso-2022-jp');
538
                });
539
            }
540
        }
541 1179
542 1179
        $this->register(new \Silex\Provider\SwiftmailerServiceProvider());
543
        $this['swiftmailer.options'] = $this['config']['mail'];
544 1179
545 1179
        if (isset($this['config']['mail']['spool']) && is_bool($this['config']['mail']['spool'])) {
546
            $this['swiftmailer.use_spool'] = $this['config']['mail']['spool'];
547
        }
548 1179
        // デフォルトはsmtpを使用
549 1179
        $transport = $this['config']['mail']['transport'];
550
        if ($transport == 'sendmail') {
551 1179
            $this['swiftmailer.transport'] = \Swift_SendmailTransport::newInstance();
552
        } elseif ($transport == 'mail') {
553
            $this['swiftmailer.transport'] = \Swift_MailTransport::newInstance();
554
        }
555
    }
556 1179
557
    public function initDoctrine()
558 1179
    {
559 1179
        $this->register(new EntityEventServiceProvider());
560
        $this->register(new \Silex\Provider\DoctrineServiceProvider(), array(
561 1179
            'dbs.options' => array(
562
                'default' => $this['config']['database']
563
            )
564
        ));
565 1179
        $this->register(new \Saxulum\DoctrineOrmManagerRegistry\Provider\DoctrineOrmManagerRegistryProvider());
566 1179
567
        // UTCで保存
568
        // @see http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/cookbook/working-with-datetime.html
569
        Type::overrideType('datetime', UTCDateTimeType::class);
570 1179
        Type::overrideType('datetimetz', UTCDateTimeType::class);
571 1179
572 1179
        // プラグインのmetadata定義を合わせて行う.
573
        $pluginConfigs = PluginConfigManager::getPluginConfigAll($this['debug']);
574
        $ormMappings = array();
575
        $ormMappings[] = array(
576
             'type' => 'annotation',
577
             'namespace' => 'Eccube\Entity',
578
             'path' => array(
579
                 __DIR__.'/Entity'
580
             ),
581
             'use_simple_annotation_reader' => false,
582
         );
583
584
        // TODO namespace は暫定
585
        $ormMappings[] = array(
586
            'type' => 'annotation',
587
            'namespace' => 'Acme\Entity',
588
            'path' => array(
589
                __DIR__.'/../../app/Acme/Entity',
590
            ),
591
            'use_simple_annotation_reader' => false,
592
        );
593 1179
594
        foreach ($pluginConfigs as $code) {
595
            $config = $code['config'];
596
            // Doctrine Extend
597
            if (isset($config['orm.path']) && is_array($config['orm.path'])) {
598
                $paths = array();
599 View Code Duplication
                foreach ($config['orm.path'] as $path) {
600
                    $paths[] = $this['config']['plugin_realdir'].'/'.$config['code'].$path;
601
                }
602 1179
                $ormMappings[] = array(
603 1179
                    'type' => 'yml',
604
                    'namespace' => 'Plugin\\'.$config['code'].'\\Entity',
605 1179
                    'path' => $paths,
606 1179
                );
607 1179
                $ormMappings[] = array(
608 1179
                    'type' => 'annotation',
609
                    'namespace' => 'Plugin\\'.$config['code'].'\\Entity',
610 1179
                    'path' => $paths,
611 1179
                    'use_simple_annotation_reader' => false,
612 1179
                );
613 1179
            }
614
        }
615 1179
616 1179
        $options = array(
617 1179
            'mappings' => $ormMappings
618 1179
        );
619
620
        if (!$this['debug']) {
621
            $cacheDrivers = array();
622
            if (array_key_exists('doctrine_cache', $this['config'])) {
623
                $cacheDrivers = $this['config']['doctrine_cache'];
624
            }
625 1179
626
            if (array_key_exists('metadata_cache', $cacheDrivers)) {
627
                $options['metadata_cache'] = $cacheDrivers['metadata_cache'];
628 1179
            }
629
            if (array_key_exists('query_cache', $cacheDrivers)) {
630
                $options['query_cache'] = $cacheDrivers['query_cache'];
631
            }
632
            if (array_key_exists('result_cache', $cacheDrivers)) {
633
                $options['result_cache'] = $cacheDrivers['result_cache'];
634
            }
635
            if (array_key_exists('hydration_cache', $cacheDrivers)) {
636
                $options['hydration_cache'] = $cacheDrivers['hydration_cache'];
637
            }
638
        }
639
640
        $this->register(new \Dflydev\Provider\DoctrineOrm\DoctrineOrmServiceProvider(), array(
641
            'orm.proxies_dir' => __DIR__.'/../../app/cache/doctrine/proxies',
642
            'orm.em.options' => $options,
643
            'orm.custom.functions.string' => array(
644
                'NORMALIZE' => 'Eccube\Doctrine\ORM\Query\Normalize',
645
            ),
646
            'orm.custom.functions.numeric' => array(
647
                'EXTRACT' => 'Eccube\Doctrine\ORM\Query\Extract',
648 1179
            ),
649 1179
        ));
650 1179
651 1179
        $this->extend(
652
            'orm.em.config',
653
            function (\Doctrine\ORM\Configuration $config, \Silex\Application $app) {
654
655
                /** @var $chain \Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain */
656
                $chain = $config->getMetadataDriverImpl();
657
                $drivers = $chain->getDrivers();
658
                foreach ($drivers as $namespace => $oldDriver) {
659
                    if ('Eccube\Entity' === $namespace) {
660
                        $newDriver = new AnnotationDriver(
661 1179
                            new CachedReader(new AnnotationReader(), new ArrayCache()),
662 1179
                            $oldDriver->getPaths());
663 1179
                        $newDriver->setFileExtension($oldDriver->getFileExtension());
664 1179
                        $newDriver->addExcludePaths($oldDriver->getExcludePaths());
665
                        $newDriver->setTraitProxiesDirectory(
666
                            realpath(__DIR__.'/../../app/proxy/entity'));
667 1179
                        $chain->addDriver($newDriver, $namespace);
668 1179
                    }
669
                }
670
671 1179
                return $config;
672 1179
            }
673
        );
674
675 1179
        $this->extend('orm.em', function (\Doctrine\ORM\EntityManager $em, \Silex\Application $app) {
676 1179
            // tax_rule
677 1179
            $taxRuleRepository = $em->getRepository('Eccube\Entity\TaxRule');
678 1179
            $taxRuleRepository->setApplication($app);
679 1179
            $taxRuleService = new \Eccube\Service\TaxRuleService($taxRuleRepository);
680
            $em->getEventManager()->addEventSubscriber(new \Eccube\Doctrine\EventSubscriber\TaxRuleEventSubscriber($taxRuleService));
681 1179
682 1179
            // save
683
            $saveEventSubscriber = new \Eccube\Doctrine\EventSubscriber\SaveEventSubscriber($app);
684
            $em->getEventManager()->addEventSubscriber($saveEventSubscriber);
685
686 1179
            // timezone
687
            $timezoneSubscriber = new \Eccube\Doctrine\EventSubscriber\TimeZoneSubscriber($app);
688 1179
            $em->getEventManager()->addEventSubscriber($timezoneSubscriber);
689 1179
690 1179
            // clear cache
691
            $clearCacheEventSubscriber = new \Eccube\Doctrine\EventSubscriber\ClearCacheEventSubscriber($app);
692 1179
            $em->getEventManager()->addEventSubscriber($clearCacheEventSubscriber);
693
694 1179
            // filters
695
            $config = $em->getConfiguration();
696 1179
            $config->addFilter("soft_delete", '\Eccube\Doctrine\Filter\SoftDeleteFilter');
697 1179
            $config->addFilter("nostock_hidden", '\Eccube\Doctrine\Filter\NoStockHiddenFilter');
698 1179
            $config->addFilter("incomplete_order_status_hidden", '\Eccube\Doctrine\Filter\OrderStatusFilter');
699 1179
            $em->getFilters()->enable('soft_delete');
700
701
            return $em;
702
        });
703
704 1179
        if (!$this['debug']) {
705 1179
            // second level cacheの設定.
706
            $this->extend(
707 1179
                'orm.em.config',
708
                function (\Doctrine\ORM\Configuration $config, \Silex\Application $app) {
709 1179
                    $config->setSecondLevelCacheEnabled();
710
                    $cacheConfig = $config->getSecondLevelCacheConfiguration();
711 1179
                    $regionConfig = $cacheConfig->getRegionsConfiguration();
712
                    // TODO キャッシュ先は設定で切り替えられるように
713
                    $cache = $this['orm.cache.factory'](
714
                        'filesystem',
715
                        [
716
                            'path' => __DIR__.'/../../app/cache/doctrine/second'
717
                        ]
718
                    );
719
                    $factory = new \Doctrine\ORM\Cache\DefaultCacheFactory($regionConfig, $cache);
720
                    $cacheConfig->setCacheFactory($factory);
721
722
                    return $config;
723
                }
724
            );
725 1179
        }
726 1179
    }
727
728
    public function initSecurity()
729 1179
    {
730 1179
        $this->register(new \Silex\Provider\SecurityServiceProvider());
731
        $this->register(new \Silex\Provider\CsrfServiceProvider());
732
        $this->register(new \Silex\Provider\RememberMeServiceProvider());
733 1179
734
        $this['security.firewalls'] = array(
735 1179
            'admin' => array(
736
                'pattern' => "^/{$this['config']['admin_route']}/",
737
                'form' => array(
738
                    'login_path' => "/{$this['config']['admin_route']}/login",
739
                    'check_path' => "/{$this['config']['admin_route']}/login_check",
740 1179
                    'username_parameter' => 'login_id',
741 1179
                    'password_parameter' => 'password',
742 1179
                    'with_csrf' => true,
743
                    'use_forward' => true,
744
                ),
745
                'logout' => array(
746
                    'logout_path' => "/{$this['config']['admin_route']}/logout",
747
                    'target_url' => "/{$this['config']['admin_route']}/",
748
                ),
749
                'users' => $this['orm.em']->getRepository('Eccube\Entity\Member'),
750 1175
                'anonymous' => true,
751
            ),
752
            'customer' => array(
753 1175
                'pattern' => '^/',
754 1175
                'form' => array(
755 1175
                    'login_path' => '/mypage/login',
756
                    'check_path' => '/login_check',
757
                    'username_parameter' => 'login_email',
758
                    'password_parameter' => 'login_pass',
759 1179
                    'with_csrf' => true,
760
                    'use_forward' => true,
761
                ),
762
                'logout' => array(
763
                    'logout_path' => '/logout',
764 1175
                    'target_url' => '/',
765
                ),
766
                'remember_me' => array(
767
                    'key' => sha1($this['config']['auth_magic']),
768 1175
                    'name' => $this['config']['cookie_name'].'_rememberme',
769
                    // lifetimeはデフォルトの1年間にする
770 1175
                    // 'lifetime' => $this['config']['cookie_lifetime'],
771 1179
                    'path' => $this['config']['root_urlpath'] ?: '/',
772
                    'secure' => $this['config']['force_ssl'],
773
                    'httponly' => true,
774 1175
                    'always_remember_me' => false,
775
                    'remember_me_parameter' => 'login_memory',
776
                ),
777 1179
                'users' => $this['orm.em']->getRepository('Eccube\Entity\Customer'),
778
                'anonymous' => true,
779
            ),
780 1175
        );
781
782 1175
        $channel = null;
783
        // 強制SSL
784
        if ($this['config']['force_ssl'] == \Eccube\Common\Constant::ENABLED) {
785 1175
            $channel = "https";
786 1175
        }
787
788
        $this['security.access_rules'] = array(
789
            array("^/{$this['config']['admin_route']}/login", 'IS_AUTHENTICATED_ANONYMOUSLY', $channel),
790
            array("^/{$this['config']['admin_route']}/", 'ROLE_ADMIN', $channel),
791
            array('^/mypage/login', 'IS_AUTHENTICATED_ANONYMOUSLY', $channel),
792
            array('^/mypage/withdraw_complete', 'IS_AUTHENTICATED_ANONYMOUSLY', $channel),
793
            array('^/mypage/change', 'IS_AUTHENTICATED_FULLY', $channel),
794 1169
            array('^/mypage', 'ROLE_USER', $channel),
795
        );
796 1169
797
        $this['eccube.password_encoder'] = function ($app) {
798
            return new \Eccube\Security\Core\Encoder\PasswordEncoder($app['config']);
799
        };
800
        $this['security.encoder_factory'] = function ($app) {
801
            return new \Symfony\Component\Security\Core\Encoder\EncoderFactory(array(
802
                'Eccube\Entity\Customer' => $app['eccube.password_encoder'],
803
                'Eccube\Entity\Member' => $app['eccube.password_encoder'],
804 475
            ));
805
        };
806 475
        $this['eccube.event_listner.security'] = function ($app) {
807
            return new \Eccube\EventListener\SecurityEventListener($app['orm.em']);
808
        };
809
810
        // Voterの設定
811
        $this['authority_voter'] = function ($app) {
812
            return new \Eccube\Security\Voter\AuthorityVoter($app);
813
        };
814
815
        $this->extend('security.voters', function ($voters, \Silex\Application $app) {
816
            $voters[] = $app['authority_voter'];
817
818
            return $voters;
819 1179
        });
820
821 1179
        $this['security.access_manager'] = function ($app) {
822 1179
            return new \Symfony\Component\Security\Core\Authorization\AccessDecisionManager($app['security.voters'], 'unanimous');
823
        };
824
825
        $this->on(\Symfony\Component\Security\Http\SecurityEvents::INTERACTIVE_LOGIN, array($this['eccube.event_listner.security'], 'onInteractiveLogin'));
826
    }
827
828
    /**
829
     * ロードバランサー、プロキシサーバの設定を行う
830
     */
831
    public function initProxy()
832
    {
833
        $config = $this['config'];
834
        if (isset($config['trusted_proxies_connection_only']) && !empty($config['trusted_proxies_connection_only'])) {
835
            $this->on(KernelEvents::REQUEST, function (GetResponseEvent $event) use ($config) {
836
                // サブリクエストのREMOTE_ADDRも動的に設定を行う必要があるため、KernelEvents::REQUESTを使用する
837
                Request::setTrustedProxies(array_merge(array($event->getRequest()->server->get('REMOTE_ADDR')), $config['trusted_proxies']));
838
            }, self::EARLY_EVENT);
839 View Code Duplication
        } elseif (isset($config['trusted_proxies']) && !empty($config['trusted_proxies'])) {
840
            Request::setTrustedProxies($config['trusted_proxies']);
841
        }
842
    }
843
844
    public function initializePlugin()
845
    {
846
        if ($this->initializedPlugin) {
847
            return;
848
        }
849
        $this->register(new ServiceProvider\EccubePluginServiceProvider());
850
        $this->initializedPlugin = true;
851
    }
852
853
    /**
854
     * PHPUnit を実行中かどうかを設定する.
855
     *
856
     * @param boolean $testMode PHPUnit を実行中の場合 true
857 1179
     */
858
    public function setTestMode($testMode)
859 1179
    {
860 1179
        $this->testMode = $testMode;
861 1179
    }
862 1179
863 1179
    /**
864 1179
     * PHPUnit を実行中かどうか.
865 1179
     *
866 1179
     * @return boolean PHPUnit を実行中の場合 true
867 1179
     */
868 1179
    public function isTestMode()
869 1179
    {
870
        return $this->testMode;
871
    }
872
873
    /**
874
     *
875
     * データベースの接続を確認
876 1179
     * 成功 : trueを返却
877 1179
     * 失敗 : \Doctrine\DBAL\DBALExceptionエラーが発生( 接続に失敗した場合 )、エラー画面を表示しdie()
878 1179
     * 備考 : app['debug']がtrueの際は処理を行わない
879 1179
     *
880 1179
     * @return boolean true
881 1179
     *
882 1179
     */
883 1179
    protected function checkDatabaseConnection()
884
    {
885
        if ($this['debug']) {
886
            return;
887
        }
888
        try {
889
            $this['db']->connect();
890 1179
        } catch (\Doctrine\DBAL\DBALException $e) {
891 1179
            $this['monolog']->error($e->getMessage());
892
            $this['twig.path'] = array(__DIR__.'/Resource/template/exception');
893 1179
            $html = $this['twig']->render('error.twig', array(
894
                'error_title' => 'データーベース接続エラー',
895
                'error_message' => 'データーベースを確認してください',
896 1179
            ));
897
            $response = new Response();
898
            $response->setContent($html);
899
            $response->setStatusCode('500');
900
            $response->headers->set('Content-Type', 'text/html');
901
            $response->send();
902
            die();
903
        }
904
905 1179
        return true;
906
    }
907 1179
908 1179
    /**
909 1179
     * Config ファイルをパースし、連想配列を返します.
910
     *
911
     * $config_name.yml ファイルをパースし、連想配列を返します.
912
     * $config_name.php が存在する場合は、 PHP ファイルに記述された連想配列を使用します。
913
     *
914
     * @param string $config_name Config 名称
915
     * @param array $configAll Config の連想配列
916
     * @param boolean $wrap_key Config の連想配列に config_name のキーを生成する場合 true, デフォルト false
917
     * @param string $ymlPath config yaml を格納したディレクトリ
918
     * @param string $distPath config yaml dist を格納したディレクトリ
919
     * @return Application
920
     */
921 1179
    public function parseConfig($config_name, array &$configAll, $wrap_key = false, $ymlPath = null, $distPath = null)
922
    {
923
        $ymlPath = $ymlPath ? $ymlPath : __DIR__.'/../../app/config/eccube';
924 1179
        $distPath = $distPath ? $distPath : __DIR__.'/../../src/Eccube/Resource/config';
925 1179
        $config = array();
926
        $config_php = $ymlPath.'/'.$config_name.'.php';
927
        if (!file_exists($config_php)) {
928
            $config_yml = $ymlPath.'/'.$config_name.'.yml';
929
            if (file_exists($config_yml)) {
930
                $config = Yaml::parse(file_get_contents($config_yml));
931
                $config = empty($config) ? array() : $config;
932 View Code Duplication
                if (isset($this['output_config_php']) && $this['output_config_php']) {
933
                    file_put_contents($config_php, sprintf('<?php return %s', var_export($config, true)).';');
934
                }
935
            }
936
        } else {
937
            $config = require $config_php;
938
        }
939
940
        // `%ROOT_DIR%`を絶対パスに変換
941
        $rootDir = realpath(__DIR__.'/../../');
942
        array_walk($config, function(&$value) use ($rootDir) {
943
            $value = str_replace('%ROOT_DIR%', $rootDir, $value);
944
        });
945
946
        $config_dist = array();
947
        $config_php_dist = $distPath.'/'.$config_name.'.dist.php';
948
        if (!file_exists($config_php_dist)) {
949
            $config_yml_dist = $distPath.'/'.$config_name.'.yml.dist';
950
            if (file_exists($config_yml_dist)) {
951
                $config_dist = Yaml::parse(file_get_contents($config_yml_dist));
952 View Code Duplication
                if (isset($this['output_config_php']) && $this['output_config_php']) {
953
                    file_put_contents($config_php_dist, sprintf('<?php return %s', var_export($config_dist, true)).';');
954
                }
955
            }
956
        } else {
957
            $config_dist = require $config_php_dist;
958
        }
959
960
        if ($wrap_key) {
961
            $configAll = array_replace_recursive($configAll, array($config_name => $config_dist), array($config_name => $config));
962
        } else {
963
            $configAll = array_replace_recursive($configAll, $config_dist, $config);
964
        }
965
966
        return $this;
967
    }
968
969
    /**
970
     * セッションが開始されているかどうか.
971
     *
972
     * @return boolean セッションが開始済みの場合 true
973
     * @link http://php.net/manual/ja/function.session-status.php#113468
974
     */
975
    protected function isSessionStarted()
976
    {
977
        if (php_sapi_name() !== 'cli') {
978
            if (version_compare(phpversion(), '5.4.0', '>=')) {
979
                return session_status() === PHP_SESSION_ACTIVE ? true : false;
980
            } else {
981
                return session_id() === '' ? false : true;
982
            }
983
        }
984
985
        return false;
986
    }
987
988
    /**
989
     * Http Cache対応
990
     */
991
    protected function initCacheRequest()
992
    {
993
        // httpキャッシュが無効の場合はイベント設定を行わない.
994
        if (!$this['config']['http_cache']['enabled']) {
995
            return;
996
        }
997
998
        $app = $this;
999
1000
        // Response Event(http cache対応、event実行は一番遅く設定)
1001
        $this->on(\Symfony\Component\HttpKernel\KernelEvents::RESPONSE, function (\Symfony\Component\HttpKernel\Event\FilterResponseEvent $event) use ($app) {
1002
1003
            if (!$event->isMasterRequest()) {
1004
                return;
1005
            }
1006
1007
            $request = $event->getRequest();
1008
            $response = $event->getResponse();
1009
1010
            $route = $request->attributes->get('_route');
1011
1012
            $etag = md5($response->getContent());
1013
1014
            if (strpos($route, 'admin') === 0) {
1015
                // 管理画面
1016
1017
                // 管理画面ではコンテンツの中身が変更された時点でキャッシュを更新し、キャッシュの適用範囲はprivateに設定
1018
                $response->setCache(array(
1019
                    'etag' => $etag,
1020
                    'private' => true,
1021
                ));
1022
1023
                if ($response->isNotModified($request)) {
1024
                    return $response;
1025
                }
1026
1027
            } else {
1028
                // フロント画面
1029
                $cacheRoute = $app['config']['http_cache']['route'];
1030
1031
                if (in_array($route, $cacheRoute) === true) {
1032
                    // キャッシュ対象となる画面lが含まれていた場合、キャッシュ化
1033
                    // max-ageを設定しているためExpiresは不要
1034
                    // Last-Modifiedだと比較する項目がないためETagで対応
1035
                    // max-ageを設定していた場合、contentの中身が変更されても変更されない
1036
1037
                    $age = $app['config']['http_cache']['age'];
1038
1039
                    $response->setCache(array(
1040
                        'etag' => $etag,
1041
                        'max_age' => $age,
1042
                        's_maxage' => $age,
1043
                        'public' => true,
1044
                    ));
1045
1046
                    if ($response->isNotModified($request)) {
1047
                        return $response;
1048
                    }
1049
                }
1050
            }
1051
1052
        }, -1024);
1053
    }
1054
}
1055