Completed
Push — 1.x ( abde83...7c6f23 )
by Akihito
13s
created

Bootstrap::getCache()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 4

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.2
cc 4
eloc 4
nc 8
nop 3
crap 4
1
<?php
2
/**
3
 * This file is part of the BEAR.Package package.
4
 *
5
 * @license http://opensource.org/licenses/MIT MIT
6
 */
7
namespace BEAR\Package;
8
9
use BEAR\AppMeta\AbstractAppMeta;
10
use BEAR\AppMeta\AppMeta;
11
use BEAR\Resource\ResourceInterface;
12
use BEAR\Sunday\Extension\Application\AbstractApp;
13
use BEAR\Sunday\Extension\Application\AppInterface;
14
use Doctrine\Common\Annotations\Reader;
15
use Doctrine\Common\Cache\ApcuCache;
16
use Doctrine\Common\Cache\Cache;
17
use Doctrine\Common\Cache\ChainCache;
18
use Doctrine\Common\Cache\FilesystemCache;
19
use Doctrine\Common\Cache\VoidCache;
20
use Psr\Log\LoggerInterface;
21
use Ray\Compiler\ScriptInjector;
22
23
/**
24
 * Bootstrap
25
 *
26
 * Create an app object that contains all the objects used in the bootstrap script The bootstrap script uses the public
27
 * property of $ app to run the application.
28
 *
29
 * AppModule knows the binding of all interfaces. Other context modules override bindings on the interface. For example,
30
 * `app` binds JsonRenderer and outputs JSON. In` html-prod`, HtmlModule overwrites the binding on TwigRenderer and
31
 * outputs html.
32
 */
33
final class Bootstrap
34
{
35
    /**
36
     * Return application instance by name and contexts
37
     *
38
     * Use newApp() instead for your own AppMeta and Cache.
39
     *
40
     * @param string $name     application name    'koriym\blog' (vendor\package)
41
     * @param string $contexts application context 'prd-html-app'
42
     * @param string $appDir   application path
43
     */
44 3
    public function getApp(string $name, string $contexts, string $appDir = null) : AbstractApp
45
    {
46 3
        return $this->newApp(new AppMeta($name, $contexts, $appDir), $contexts);
47
    }
48
49
    /**
50
     * Return cached contextual application instance
51
     */
52 6
    public function newApp(AbstractAppMeta $appMeta, string $contexts, Cache $cache = null) : AbstractApp
53
    {
54 6
        $cache = $this->getCache($appMeta, $contexts, $cache);
55 6
        $appId = $appMeta->name . $contexts . filemtime($appMeta->appDir . '/src');
56 6
        list($app) = $cache->fetch($appId); // $scriptInjector set cached single instance in wakeup
57 6
        if ($app && $app instanceof AbstractApp) {
58 1
            return $app;
59
        }
60 6
        list($app, $injector) = $this->getInstance($appMeta, $contexts);
61 5
        $cache->save($appId, [$app, $injector]); // save $app with injector to save singleton instance (in ScriptInjector::$singletons)
62
63 5
        return $app;
64
    }
65
66 6
    private function getInstance(AbstractAppMeta $appMeta, string $contexts) : array
67
    {
68 6
        $app = (new AppInjector($appMeta->name, $contexts))->getInstance(AppInterface::class);
69 5
        $injector = new ScriptInjector($appMeta->tmpDir);
70
        // save singleton instance cache
71 5
        $injector->getInstance(Reader::class);
72 5
        $injector->getInstance(Cache::class);
73 5
        $injector->getInstance(LoggerInterface::class);
74 5
        $injector->getInstance(ResourceInterface::class);
75 5
        $log = sprintf('%s/context.%s.log', $appMeta->logDir, $contexts);
76 5
        file_put_contents($log, print_r($app, true));
77
78 5
        return [$app, $injector];
79
    }
80
81 6
    private function getCache(AbstractAppMeta $appMeta, string $contexts, Cache $cache = null) : Cache
82
    {
83 6
        $isCacheable = is_int(strpos($contexts, 'prod-')) || is_int(strpos($contexts, 'stage-'));
84 6
        $cache = $cache ?: ($isCacheable ? new ChainCache([new ApcuCache, new FilesystemCache($appMeta->tmpDir)]) : new VoidCache);
85
86 6
        return $cache;
87
    }
88
}
89