Completed
Push — master ( 8032e3...4751f1 )
by Morris
15:26
created

IconsCacher::getCachedCSS()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
declare (strict_types = 1);
3
/**
4
 * @copyright Copyright (c) 2018, John Molakvoæ ([email protected])
5
 *
6
 * @author John Molakvoæ (skjnldsv) <[email protected]>
7
 *
8
 * @license GNU AGPL version 3 or any later version
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License as
12
 * published by the Free Software Foundation, either version 3 of the
13
 * License, or (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 *
23
 */
24
25
namespace OC\Template;
26
27
use OCP\Files\IAppData;
28
use OCP\Files\NotFoundException;
29
use OCP\Files\SimpleFS\ISimpleFolder;
30
use OCP\Files\SimpleFS\ISimpleFile;
31
use OCP\ILogger;
32
use OCP\IURLGenerator;
33
use OC\Files\AppData\Factory;
34
35
class IconsCacher {
36
37
	/** @var ILogger */
38
	protected $logger;
39
40
	/** @var IAppData */
41
	protected $appData;
42
43
	/** @var ISimpleFolder */
44
	private $folder;
45
46
	/** @var IURLGenerator */
47
	protected $urlGenerator;
48
49
	/** @var string */
50
	private $iconVarRE = '/--(icon-[a-z0-9-]+): url\(["\']([a-z0-9-_\~\/\?\&\=\.]+)[^;]+;/m';
51
52
	/** @var string */
53
	private $fileName = 'icons-vars.css';
54
55
	/**
56
	 * @param ILogger $logger
57
	 * @param Factory $appDataFactory
58
	 * @param IURLGenerator $urlGenerator
59
	 */
60
	public function __construct(ILogger $logger,
61
								Factory $appDataFactory,
62
								IURLGenerator $urlGenerator) {
63
		$this->logger       = $logger;
64
		$this->appData      = $appDataFactory->get('css');
65
		$this->urlGenerator = $urlGenerator;
66
67
		try {
68
			$this->folder = $this->appData->getFolder('icons');
69
		} catch (NotFoundException $e) {
70
			$this->folder = $this->appData->newFolder('icons');
71
		}
72
	}
73
74
	private function getIconsFromCss(string $css): array{
75
		preg_match_all($this->iconVarRE, $css, $matches, PREG_SET_ORDER);
76
		$icons = [];
77
		foreach ($matches as $icon) {
0 ignored issues
show
Bug introduced by
The expression $matches of type null|array<integer,array<integer,string>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
78
			$icons[$icon[1]] = $icon[2];
79
		}
80
81
		return $icons;
82
	}
83
	/**
84
	 * Parse and cache css
85
	 *
86
	 * @param string $css
87
	 */
88
	public function setIconsCss(string $css) {
89
90
		$cachedFile = $this->getCachedCSS();
91
		if (!$cachedFile) {
92
			$currentData = '';
93
		} else {
94
			$currentData = $cachedFile->getContent();
95
		}
96
97
		// remove :root
98
		$currentData = str_replace([':root {', '}'], '', $currentData);
99
100
		$icons = $this->getIconsFromCss($currentData . $css);
101
102
		$data = '';
103
		foreach ($icons as $icon => $url) {
104
			$data .= "--$icon: url('$url?v=1');";
105
		}
106
107
		if (strlen($data) > 0) {
108
			if (!$cachedFile) {
109
				$cachedFile = $this->folder->newFile($this->fileName);
110
			}
111
112
			$data = ":root {
113
				$data
114
			}";
115
			$cachedFile->putContent($data);
116
		}
117
118
		return preg_replace($this->iconVarRE, '', $css);
119
	}
120
121
	/**
122
	 * Get icons css file
123
	 * @return ISimpleFile|boolean
124
	 */
125
	public function getCachedCSS() {
126
		try {
127
			return $this->folder->getFile($this->fileName);
128
		} catch (NotFoundException $e) {
129
			return false;
130
		}
131
	}
132
133
	public function injectCss() {
134
		// Only inject once
135
		foreach (\OC_Util::$headers as $header) {
136
			if (
137
				array_key_exists('attributes', $header) &&
138
				array_key_exists('href', $header['attributes']) &&
139
				strpos($header['attributes']['href'], $this->fileName) !== false) {
140
				return;
141
			}
142
		}
143
		$linkToCSS = substr($this->urlGenerator->linkToRoute('core.Css.getCss', ['appName' => 'icons', 'fileName' => $this->fileName]), strlen(\OC::$WEBROOT));
144
		\OC_Util::addHeader('link', ['rel' => 'stylesheet', 'href' => $linkToCSS], null, true);
145
	}
146
147
}