Passed
Push — develop ( ab628c...193991 )
by Brent
04:14
created

Stitcher::prepareCdn()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 11
nc 3
nop 0
dl 0
loc 17
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
namespace Brendt\Stitcher;
4
5
use AsyncInterop\Promise;
6
use Brendt\Stitcher\Exception\TemplateNotFoundException;
7
use Brendt\Stitcher\Site\Site;
8
use Symfony\Component\Config\FileLocator;
9
use Symfony\Component\DependencyInjection\ContainerBuilder;
10
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
11
use Symfony\Component\Filesystem\Filesystem;
12
use Symfony\Component\Finder\Finder;
13
use Symfony\Component\Finder\SplFileInfo;
14
use Symfony\Component\Yaml\Yaml;
15
16
/**
17
 * The Stitcher class is the core compiler of every Stitcher application. This class takes care of all routes, pages,
18
 * templates and data, and "stitches" everything together.
19
 *
20
 * The stitching process is done in several steps, with the final result being a fully rendered website in the
21
 * `directories.public` folder.
22
 */
23
class Stitcher
24
{
25
    /**
26
     * @var ContainerBuilder
27
     */
28
    protected static $container;
29
30
    /**
31
     * @var array
32
     */
33
    protected static $configDefaults = [
34
        'directories.src'    => './src',
35
        'directories.public' => './public',
36
        'directories.cache'  => './.cache',
37
        'meta'               => [],
38
        'minify'             => false,
39
        'engines.template'   => 'smarty',
40
        'engines.image'      => 'gd',
41
        'engines.optimizer'  => true,
42
        'engines.async'      => true,
43
        'cdn'                => [],
44
        'caches.image'       => true,
45
        'caches.cdn'         => true,
46
        'optimizer.options'  => [],
47
    ];
48
49
    /**
50
     * @var string
51
     */
52
    private $srcDir;
53
54
    /**
55
     * @var string
56
     */
57
    private $publicDir;
58
59
    /**
60
     * @var string
61
     */
62
    private $templateDir;
63
64
    /**
65
     * @var SiteParser
66
     */
67
    private $siteParser;
68
69
    /**
70
     * @see \Brendt\Stitcher\Stitcher::create()
71
     *
72
     * @param string $srcDir
73
     * @param string $publicDir
74
     * @param string $templateDir
75
     */
76
    private function __construct(?string $srcDir = './src', ?string $publicDir = './public', ?string $templateDir = './src/template') {
77
        $this->srcDir = $srcDir;
78
        $this->publicDir = $publicDir;
79
        $this->templateDir = $templateDir;
80
    }
81
82
    /**
83
     * Static constructor
84
     *
85
     * @param string $configPath
86
     * @param array  $defaultConfig
87
     *
88
     * @return Stitcher
89
     *
90
     */
91
    public static function create(string $configPath = './config.yml', array $defaultConfig = []) : Stitcher {
92
        self::$container = new ContainerBuilder();
93
94
        $configPathParts = explode('/', $configPath);
95
        $configFileName = array_pop($configPathParts);
96
        $configPath = implode('/', $configPathParts) . '/';
97
        $configFiles = Finder::create()->files()->in($configPath)->name($configFileName)->depth(0);
98
        $srcDir = null;
99
        $publicDir = null;
100
        $templateDir = null;
101
102
        /** @var SplFileInfo $configFile */
103
        foreach ($configFiles as $configFile) {
104
            $fileConfig = Yaml::parse($configFile->getContents());
105
106
            $config = array_merge(
107
                self::$configDefaults,
108
                $fileConfig,
109
                Config::flatten($fileConfig),
110
                $defaultConfig
111
            );
112
113
            $config['directories.template'] = $config['directories.template'] ?? $config['directories.src'];
114
115
            foreach ($config as $key => $value) {
116
                self::$container->setParameter($key, $value);
117
            }
118
119
            $srcDir = $config['directories.src'] ?? $srcDir;
120
            $publicDir = $config['directories.public'] ?? $publicDir;
121
            $templateDir = $config['directories.template'] ?? $templateDir;
122
        }
123
124
        $stitcher = new self($srcDir, $publicDir, $templateDir);
125
        self::$container->set('stitcher', $stitcher);
126
127
        $serviceLoader = new YamlFileLoader(self::$container, new FileLocator(__DIR__));
128
        $serviceLoader->load('services.yml');
129
130
        return $stitcher;
131
    }
132
133
    /**
134
     * @param string $id
135
     *
136
     * @return mixed
137
     */
138
    public static function get(string $id) {
139
        return self::$container->get($id);
140
    }
141
142
    /**
143
     * @param string $key
144
     *
145
     * @return mixed
146
     */
147
    public static function getParameter(string $key) {
148
        return self::$container->getParameter($key);
149
    }
150
151
    /**
152
     * The core stitcher function. This function will compile the configured site and return an array of parsed
153
     * data.
154
     *
155
     * Compiling a site is done in the following steps.
156
     *
157
     *      - Load the site configuration @see \Brendt\Stitcher\Stitcher::loadSite()
158
     *      - Load all available templates @see \Brendt\Stitcher\Stitcher::loadTemplates()
159
     *      - Loop over all pages and transform every page with the configured adapters (in any are set) @see
160
     *      \Brendt\Stitcher\Stitcher::parseAdapters()
161
     *      - Loop over all transformed pages and parse the variables which weren't parsed by the page's adapters. @see
162
     *      \Brendt\Stitcher\Stitcher::parseVariables()
163
     *      - Add all variables to the template engine and render the HTML for each page.
164
     *
165
     * This function takes two optional parameters which are used to render pages on the fly when using the
166
     * developer controller. The first one, `routes` will take a string or array of routes which should be rendered,
167
     * instead of all available routes. The second one, `filterValue` is used to provide a filter when the
168
     * CollectionAdapter is used, and only one entry page should be rendered.
169
     *
170
     * @param string|array $routes
171
     * @param string       $filterValue
172
     *
173
     * @return array
174
     * @throws TemplateNotFoundException
175
     *
176
     * @see \Brendt\Stitcher\Stitcher::save()
177
     * @see \Brendt\Stitcher\Controller\DevController::run()
178
     * @see \Brendt\Stitcher\Adapter\CollectionAdapter::transform()
179
     */
180
    public function stitch($routes = [], string $filterValue = null) : array {
181
        /** @var SiteParser $siteParser */
182
        $siteParser = self::get('parser.site');
183
184
        $this->prepareCdn();
185
186
        return $siteParser->parse($routes, $filterValue);
0 ignored issues
show
Bug introduced by
It seems like $routes defined by parameter $routes on line 180 can also be of type string; however, Brendt\Stitcher\SiteParser::parse() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
187
    }
188
189
    /**
190
     * @param array $routes
191
     *
192
     * @return Site
193
     */
194
    public function loadSite(array $routes = []) : Site {
195
        /** @var SiteParser $siteParser */
196
        $siteParser = self::get('parser.site');
197
198
        return $siteParser->loadSite($routes);
199
    }
200
201
    /**
202
     * This function will save a stitched output to HTML files in the `directories.public` directory.
203
     *
204
     * @param array $blanket
205
     *
206
     * @see \Brendt\Stitcher\Stitcher::stitch()
207
     */
208
    public function save(array $blanket) {
209
        $fs = new Filesystem();
210
211
        foreach ($blanket as $path => $page) {
212
            if ($path === '/') {
213
                $path = 'index';
214
            }
215
216
            $fs->dumpFile($this->publicDir . "/{$path}.html", $page);
217
        }
218
    }
219
220
    /**
221
     * Parse CDN resources and libraries
222
     */
223
    public function prepareCdn() {
224
        $cdn = (array) self::getParameter('cdn');
225
        $enableCache = self::getParameter('caches.cdn');
226
        $fs = new Filesystem();
227
228
        foreach ($cdn as $resource) {
229
            $resource = trim($resource, '/');
230
            $publicResourcePath = "{$this->publicDir}/{$resource}";
231
232
            if ($enableCache && $fs->exists($publicResourcePath)) {
233
                continue;
234
            }
235
236
            $sourceResourcePath = "{$this->srcDir}/{$resource}";
237
            $fs->copy($sourceResourcePath, $publicResourcePath, true);
238
        }
239
    }
240
241
}
242
243
244