3 declare(strict_types
=1);
5 namespace PhpMyAdmin\Theme
;
7 use PhpMyAdmin\Exceptions\InvalidImagePath
;
8 use PhpMyAdmin\Version
;
9 use Webmozart\Assert\Assert
;
10 use Webmozart\Assert\InvalidArgumentException
;
13 use function file_exists
;
14 use function file_get_contents
;
15 use function filemtime
;
16 use function filesize
;
17 use function in_array
;
18 use function is_array
;
20 use function is_readable
;
21 use function json_decode
;
24 use function version_compare
;
26 use const DIRECTORY_SEPARATOR
;
31 * @todo add the possibility to make a theme depend on another theme
32 * and by default on original
33 * @todo make all components optional - get missing components from 'parent' theme
37 /** @var string theme version */
38 public string $version = '0.0.0.0';
40 /** @var string theme name */
41 public string $name = '';
43 /** @var string theme id */
44 public string $id = '';
46 /** @var string theme path */
47 public string $path = '';
49 /** @var string file system theme path */
50 private string $fsPath = '';
52 /** @var string image path as an URL */
53 public string $imgPath = '';
55 /** @var string image path on the file-system */
56 public string $imgPathFs = '';
58 /** @var int last modification time for info file */
59 public int $mtimeInfo = 0;
62 * needed because sometimes, the mtime for different themes
65 * @var int filesize for info file
67 public int $filesizeInfo = 0;
69 /** @var list<non-empty-string> */
70 private array $colorModes = ['light'];
72 /** @var non-empty-string */
73 private string $colorMode = 'light';
76 * Loads theme information
78 public function loadInfo(): bool
80 $infofile = $this->getFsPath() . 'theme.json';
81 if (! @file_exists
($infofile)) {
85 if ($this->mtimeInfo
=== filemtime($infofile)) {
89 $content = @file_get_contents
($infofile);
90 if ($content === false) {
94 $data = json_decode($content, true);
96 // Did we get expected data?
97 if (! is_array($data)) {
101 // Check that all required data are there
102 $members = ['name', 'version', 'supports'];
103 foreach ($members as $member) {
104 if (! isset($data[$member])) {
110 if (! is_array($data['supports'])) {
114 if (! in_array(Version
::SERIES
, $data['supports'])) {
119 Assert
::keyExists($data, 'colorModes');
120 Assert
::isNonEmptyList($data['colorModes']);
121 Assert
::allStringNotEmpty($data['colorModes']);
122 $this->colorModes
= $data['colorModes'];
123 $this->colorMode
= $this->colorModes
[0];
124 } catch (InvalidArgumentException
) {
127 $this->mtimeInfo
= filemtime($infofile);
128 $this->filesizeInfo
= filesize($infofile);
130 $this->setVersion($data['version']);
131 $this->setName($data['name']);
136 public static function load(string $themeUrl, string $themeFsPath, string $themeName): self|
null
140 $theme->setPath($themeUrl);
141 $theme->setFsPath($themeFsPath);
143 if (! $theme->loadInfo()) {
147 $theme->checkImgPath();
148 $theme->setId($themeName);
154 * checks image path for existence - if not found use img from fallback theme
156 public function checkImgPath(): bool
158 // try current theme first
159 if (is_dir($this->getFsPath() . 'img' . DIRECTORY_SEPARATOR
)) {
160 $this->setImgPath($this->getPath() . '/img/');
161 $this->setImgPathFs($this->getFsPath() . 'img' . DIRECTORY_SEPARATOR
);
166 // try fallback theme
167 $fallbackFsPathThemeDir = ThemeManager
::getThemesFsDir() . ThemeManager
::FALLBACK_THEME
168 . DIRECTORY_SEPARATOR
. 'img' . DIRECTORY_SEPARATOR
;
169 if (is_dir($fallbackFsPathThemeDir)) {
170 $fallbackUrl = ThemeManager
::getThemesDir() . ThemeManager
::FALLBACK_THEME
172 $this->setImgPath($fallbackUrl);
173 $this->setImgPathFs($fallbackFsPathThemeDir);
178 throw new InvalidImagePath(sprintf(
179 __('No valid image path for theme %s found!'),
185 * returns path to theme
187 * @return string path to theme
189 public function getPath(): string
195 * returns file system path to the theme
197 * @return string file system path to theme
199 public function getFsPath(): string
201 return $this->fsPath
;
207 * @param string $path path to theme
209 public function setPath(string $path): void
211 $this->path
= trim($path);
215 * set file system path to the theme
217 * @param string $path path to theme
219 public function setFsPath(string $path): void
221 $this->fsPath
= trim($path);
227 * @param string $version version to set
229 public function setVersion(string $version): void
231 $this->version
= trim($version);
237 * @return string version
239 public function getVersion(): string
241 return $this->version
;
245 * checks theme version against $version
246 * returns true if theme version is equal or higher to $version
248 * @param string $version version to compare to
250 public function checkVersion(string $version): bool
252 return version_compare($this->getVersion(), $version, 'lt');
258 * @param string $name name to set
260 public function setName(string $name): void
262 $this->name
= trim($name);
268 * @return string name
270 public function getName(): string
278 * @param string $id new id
280 public function setId(string $id): void
282 $this->id
= trim($id);
290 public function getId(): string
296 * Sets path to images for the theme
298 * @param string $path path to images for this theme as an URL path
300 public function setImgPath(string $path): void
302 $this->imgPath
= $path;
306 * Sets path to images for the theme
308 * @param string $path file-system path to images for this theme
310 public function setImgPathFs(string $path): void
312 $this->imgPathFs
= $path;
316 * Returns the path to image for the theme.
317 * If filename is given, it possibly fallbacks to fallback
318 * theme for it if image does not exist.
320 * @param string|null $file file name for image
321 * @param string|null $fallback fallback image
323 * @return string image path for this theme
325 public function getImgPath(string|
null $file = null, string|
null $fallback = null): string
327 if ($file === null) {
328 return $this->imgPath
;
331 if (is_readable($this->imgPathFs
. $file)) {
332 return $this->imgPath
. $file;
335 if ($fallback !== null) {
336 return $this->getImgPath($fallback);
339 return './themes/' . ThemeManager
::FALLBACK_THEME
. '/img/' . $file;
342 /** @return list<non-empty-string> */
343 public function getColorModes(): array
345 return $this->colorModes
;
348 public function setColorMode(string $colorMode): void
350 if (! in_array($colorMode, $this->colorModes
, true)) {
354 $this->colorMode
= $colorMode;
357 /** @return non-empty-string */
358 public function getColorMode(): string
360 return $this->colorMode
;