ThemeRegistry::getThemePath()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
4
namespace TheCodingMachine\CMS\StaticRegistry\Registry;
5
6
7
use Psr\Container\ContainerInterface;
8
use Psr\SimpleCache\CacheInterface;
9
use Symfony\Component\Finder\Finder;
10
use TheCodingMachine\CMS\StaticRegistry\Loaders\SubTheme;
11
use TheCodingMachine\CMS\StaticRegistry\Loaders\Theme;
12
use TheCodingMachine\CMS\Theme\SubThemeDescriptor;
13
use TheCodingMachine\CMS\Theme\ThemeDescriptorInterface;
14
use TheCodingMachine\CMS\Theme\TwigThemeDescriptor;
15
16
/**
17
 * The theme registry can fetch Theme objects from the "theme" directory or from the container.
18
 */
19
class ThemeRegistry
20
{
21
    /**
22
     * @var string
23
     */
24
    private $themeDirectory;
25
    /**
26
     * @var string
27
     */
28
    private $subThemeDirectory;
29
    /**
30
     * @var ContainerInterface
31
     */
32
    private $container;
33
    /**
34
     * @var CacheInterface
35
     */
36
    private $cache;
37
    /**
38
     * @var SubTheme[]
39
     */
40
    private $subThemes;
41
    /**
42
     * @var BlockRegistry
43
     */
44
    private $blockRegistry;
45
46
    public function __construct(string $themeDirectory, string $subThemeDirectory, ContainerInterface $container, CacheInterface $cache, BlockRegistry $blockRegistry)
47
    {
48
        $this->themeDirectory = rtrim($themeDirectory, '/\\').'/';
49
        $this->subThemeDirectory = rtrim($subThemeDirectory, '/\\').'/';
50
        $this->container = $container;
51
        $this->cache = $cache;
52
        $this->blockRegistry = $blockRegistry;
53
    }
54
55
    public function getThemeDescriptor(string $themeName): ThemeDescriptorInterface
56
    {
57
        $key = 'theme.'.$themeName;
58
        $theme = $this->cache->get($key);
59
        if ($theme === null) {
60
            $theme = $this->getThemeNoCache($themeName);
61
            $this->cache->set($key, $theme);
62
        }
63
        return $theme;
64
    }
65
66
    private function getThemeNoCache(string $themeName): ThemeDescriptorInterface
67
    {
68
        $dir = $this->themeDirectory.$themeName;
69
        if ($this->container->has($themeName)) {
70
            $entry = $this->container->get($themeName);
71
            if ($entry instanceof Theme) {
72
                return $this->themeToBlock($entry);
73
            }
74
            if ($entry instanceof SubTheme) {
75
                return $this->subThemeToBlock($entry);
76
            }
77
            if ($entry instanceof ThemeDescriptorInterface) {
78
                return $entry;
79
            }
80
        }
81
82
        if (Theme::existsInDirectory($dir)) {
83
            return $this->themeToBlock(Theme::fromDirectory($dir));
84
        }
85
86
        $subThemes = $this->getSubThemes();
87
        if (isset($subThemes[$themeName])) {
88
            return $this->subThemeToBlock($subThemes[$themeName]);
89
        } else {
90
            throw ThemeNotFoundException::couldNotLoadTheme($themeName, $dir);
91
        }
92
    }
93
94
    private function themeToBlock(Theme $theme) : ThemeDescriptorInterface
95
    {
96
        return new TwigThemeDescriptor('index.twig', [
97
            'theme' => $theme->getName()
98
        ]);
99
    }
100
101
    private function subThemeToBlock(SubTheme $subTheme) : ThemeDescriptorInterface
102
    {
103
        $context = [];
104
        $themePath = $this->getThemePath($subTheme);
105
        foreach ($subTheme->getAssignations() as $zone => $blocks) {
106
            foreach ($blocks as $blockName) {
107
                foreach ($this->blockRegistry->getBlocks($blockName) as $block) {
108
                    $context[$zone][] = $block->toCmsBlock($themePath);
109
                }
110
            }
111
        }
112
        return new SubThemeDescriptor($this->getThemeDescriptor($subTheme->getParent()), $context);
113
    }
114
115
    private function getThemePath(SubTheme $subTheme): string
116
    {
117
        $subThemes = $this->getSubThemes();
118
        $parentName = $subTheme->getParent();
119
        while (isset($subThemes[$parentName])) {
120
            $subTheme = $subThemes[$parentName];
121
            $parentName = $subTheme->getParent();
122
        }
123
        return $parentName;
124
    }
125
126
    /**
127
     * Loads all subthemes and returns them, indexed by name.
128
     *
129
     * @return SubTheme[]
130
     * @throws \TheCodingMachine\CMS\StaticRegistry\Registry\DuplicateSubThemeException
131
     */
132
    private function getSubThemes(): array
133
    {
134
        if ($this->subThemes === null)
135
        {
136
            $this->subThemes = [];
137
            $fileList = new Finder();
138
139
            $fileList->files()->in($this->subThemeDirectory)->name('/\.yml|\.yaml/')->sortByName();
140
141
            foreach ($fileList as $splFileInfo) {
142
                $subTheme = SubTheme::fromFile($splFileInfo->getRealPath());
143
                $themeName = $subTheme->getName();
144
                if (isset($this->subThemes[$themeName])) {
145
                    throw new DuplicateSubThemeException(sprintf('The sub-theme "%s" has been found twice.', $themeName));
146
                }
147
                $this->subThemes[$themeName] = $subTheme;
148
            }
149
        }
150
        return $this->subThemes;
151
    }
152
}
153