1<?php 2/** 3 * Copyright (c) 2021. ComboStrap, Inc. and its affiliates. All Rights Reserved. 4 * 5 * This source code is licensed under the GPL license found in the 6 * COPYING file in the root directory of this source tree. 7 * 8 * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 9 * @author ComboStrap <support@combostrap.com> 10 * 11 */ 12 13namespace ComboStrap; 14 15 16use JsonSerializable; 17 18class Snippet implements JsonSerializable 19{ 20 /** 21 * The head in css format 22 * We need to add the style node 23 */ 24 const TYPE_CSS = "css"; 25 26 27 /** 28 * The head in javascript 29 * We need to wrap it in a script node 30 */ 31 const TYPE_JS = "js"; 32 /** 33 * A tag head in array format 34 * No need 35 */ 36 const TAG_TYPE = "tag"; 37 const JSON_SNIPPET_ID_PROPERTY = "id"; 38 const JSON_TYPE_PROPERTY = "type"; 39 const JSON_CRITICAL_PROPERTY = "critical"; 40 const JSON_CONTENT_PROPERTY = "content"; 41 const JSON_HEAD_PROPERTY = "head"; 42 43 private $snippetId; 44 private $type; 45 46 /** 47 * @var bool 48 */ 49 private $critical; 50 51 /** 52 * @var string the text script / style (may be null if it's an external resources) 53 */ 54 private $content; 55 /** 56 * @var array 57 */ 58 private $headsTags; 59 60 /** 61 * Snippet constructor. 62 */ 63 public function __construct($snippetId, $snippetType) 64 { 65 $this->snippetId = $snippetId; 66 $this->type = $snippetType; 67 } 68 69 public static function createJavascriptSnippet($snippetId): Snippet 70 { 71 return new Snippet($snippetId, self::TYPE_JS); 72 } 73 74 public static function createCssSnippet($snippetId): Snippet 75 { 76 return new Snippet($snippetId, self::TYPE_CSS); 77 } 78 79 /** 80 * @param $snippetId 81 * @return Snippet 82 * @deprecated You should create a snippet with a known type, this constructor was created for refactoring 83 */ 84 public static function createUnknownSnippet($snippetId): Snippet 85 { 86 return new Snippet($snippetId, "unknwon"); 87 } 88 89 90 /** 91 * @param $bool - if the snippet is critical, it would not be deferred or preloaded 92 * @return Snippet for chaining 93 * All css that are for animation or background for instance 94 * should not be set as critical as they are not needed to paint 95 * exactly the page 96 */ 97 public function setCritical($bool): Snippet 98 { 99 $this->critical = $bool; 100 return $this; 101 } 102 103 /** 104 * @param $content - Set an inline content for a script or stylesheet 105 * @return Snippet for chaining 106 */ 107 public function setContent($content): Snippet 108 { 109 $this->content = $content; 110 return $this; 111 } 112 113 /** 114 * @return string 115 */ 116 public function getContent() 117 { 118 if ($this->content == null) { 119 switch ($this->type) { 120 case self::TYPE_CSS: 121 $this->content = $this->getCssRulesFromFile($this->snippetId); 122 break; 123 case self::TYPE_JS: 124 $this->content = $this->getJavascriptContentFromFile($this->snippetId); 125 break; 126 default: 127 LogUtility::msg("The snippet ($this) has no content", LogUtility::LVL_MSG_ERROR, "support"); 128 } 129 } 130 return $this->content; 131 } 132 133 /** 134 * @param $tagName 135 * @return false|string - the css content of the css file 136 */ 137 private function getCssRulesFromFile($tagName) 138 { 139 140 $path = Resources::getSnippetResourceDirectory() . "/style/" . strtolower($tagName) . ".css"; 141 if (file_exists($path)) { 142 return file_get_contents($path); 143 } else { 144 LogUtility::msg("The css file ($path) was not found", LogUtility::LVL_MSG_WARNING, $tagName); 145 return ""; 146 } 147 148 } 149 150 /** 151 * @param $tagName - the tag name 152 * @return false|string - the specific javascript content for the tag 153 */ 154 private function getJavascriptContentFromFile($tagName) 155 { 156 157 $path = Resources::getSnippetResourceDirectory() . "/js/" . strtolower($tagName) . ".js"; 158 if (file_exists($path)) { 159 return file_get_contents($path); 160 } else { 161 LogUtility::msg("The javascript file ($path) was not found", LogUtility::LVL_MSG_WARNING, $tagName); 162 return ""; 163 } 164 165 } 166 167 public function __toString() 168 { 169 return $this->snippetId . "-" . $this->type; 170 } 171 172 /** 173 * Set all tags at once. 174 * @param array $tags 175 * @return Snippet 176 */ 177 public function setTags(array $tags): Snippet 178 { 179 $this->headsTags = $tags; 180 return $this; 181 } 182 183 public function getTags(): array 184 { 185 return $this->headsTags; 186 } 187 188 public function getCritical(): bool 189 { 190 191 if ($this->critical === null) { 192 if ($this->type == self::TYPE_CSS) { 193 // All CSS should be loaded first 194 // The CSS animation / background can set this to false 195 return true; 196 } 197 return false; 198 } 199 return $this->critical; 200 } 201 202 public function getClass(): string 203 { 204 /** 205 * The class for the snippet is just to be able to identify them 206 * 207 * The `snippet` prefix was added to be sure that the class 208 * name will not conflict with a css class 209 * Example: if you set the class to `combo-list` 210 * and that you use it in a inline `style` tag with 211 * the same class name, the inline `style` tag is not applied 212 * 213 */ 214 return "snippet-" . $this->snippetId . "-" . SnippetManager::COMBO_CLASS_SUFFIX; 215 216 } 217 218 /** 219 * @return string the HTML of the tag (works for now only with CSS content) 220 */ 221 public function getHtmlStyleTag(): string 222 { 223 $content = $this->getContent(); 224 $class = $this->getClass(); 225 return <<<EOF 226<style class="$class"> 227$content 228</style> 229EOF; 230 231 } 232 233 public function getId() 234 { 235 return $this->snippetId; 236 } 237 238 239 public function jsonSerialize(): array 240 { 241 $dataToSerialize = [ 242 self::JSON_SNIPPET_ID_PROPERTY => $this->snippetId, 243 self::JSON_TYPE_PROPERTY => $this->type 244 ]; 245 if ($this->critical !== null) { 246 $dataToSerialize[self::JSON_CRITICAL_PROPERTY] = $this->critical; 247 } 248 if ($this->content !== null) { 249 $dataToSerialize[self::JSON_CONTENT_PROPERTY] = $this->content; 250 } 251 if ($this->headsTags !== null) { 252 $dataToSerialize[self::JSON_HEAD_PROPERTY] = $this->headsTags; 253 } 254 return $dataToSerialize; 255 256 } 257 258 /** 259 * @throws ExceptionCombo 260 */ 261 public static function createFromJson($array): Snippet 262 { 263 $snippetId = $array[self::JSON_SNIPPET_ID_PROPERTY]; 264 if ($snippetId === null) { 265 throw new ExceptionCombo("The snippet id property was not found in the json array"); 266 } 267 $type = $array[self::JSON_TYPE_PROPERTY]; 268 if ($type === null) { 269 throw new ExceptionCombo("The snippet type property was not found in the json array"); 270 } 271 $snippet = new Snippet($snippetId, $type); 272 $critical = $array[self::JSON_CRITICAL_PROPERTY]; 273 if ($critical !== null) { 274 $snippet->setCritical($critical); 275 } 276 277 $content = $array[self::JSON_CONTENT_PROPERTY]; 278 if ($content !== null) { 279 $snippet->setContent($content); 280 } 281 282 $heads = $array[self::JSON_HEAD_PROPERTY]; 283 if ($heads !== null) { 284 $snippet->setTags($heads); 285 } 286 return $snippet; 287 288 } 289} 290