137748cd8SNickeau<?php 237748cd8SNickeau/** 337748cd8SNickeau * Copyright (c) 2021. ComboStrap, Inc. and its affiliates. All Rights Reserved. 437748cd8SNickeau * 537748cd8SNickeau * This source code is licensed under the GPL license found in the 637748cd8SNickeau * COPYING file in the root directory of this source tree. 737748cd8SNickeau * 837748cd8SNickeau * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 937748cd8SNickeau * @author ComboStrap <support@combostrap.com> 1037748cd8SNickeau * 1137748cd8SNickeau */ 1237748cd8SNickeau 1337748cd8SNickeaunamespace ComboStrap; 1437748cd8SNickeau 1537748cd8SNickeau 16*c3437056SNickeauuse JsonSerializable; 17*c3437056SNickeau 18*c3437056SNickeauclass Snippet implements JsonSerializable 1937748cd8SNickeau{ 2037748cd8SNickeau /** 2137748cd8SNickeau * The head in css format 2237748cd8SNickeau * We need to add the style node 2337748cd8SNickeau */ 2437748cd8SNickeau const TYPE_CSS = "css"; 2537748cd8SNickeau 26*c3437056SNickeau 2737748cd8SNickeau /** 2837748cd8SNickeau * The head in javascript 2937748cd8SNickeau * We need to wrap it in a script node 3037748cd8SNickeau */ 3137748cd8SNickeau const TYPE_JS = "js"; 3237748cd8SNickeau /** 3337748cd8SNickeau * A tag head in array format 3437748cd8SNickeau * No need 3537748cd8SNickeau */ 3637748cd8SNickeau const TAG_TYPE = "tag"; 37*c3437056SNickeau const JSON_SNIPPET_ID_PROPERTY = "id"; 38*c3437056SNickeau const JSON_TYPE_PROPERTY = "type"; 39*c3437056SNickeau const JSON_CRITICAL_PROPERTY = "critical"; 40*c3437056SNickeau const JSON_CONTENT_PROPERTY = "content"; 41*c3437056SNickeau const JSON_HEAD_PROPERTY = "head"; 4237748cd8SNickeau 4337748cd8SNickeau private $snippetId; 4437748cd8SNickeau private $type; 4537748cd8SNickeau 4637748cd8SNickeau /** 4737748cd8SNickeau * @var bool 4837748cd8SNickeau */ 49*c3437056SNickeau private $critical; 5037748cd8SNickeau 5137748cd8SNickeau /** 5237748cd8SNickeau * @var string the text script / style (may be null if it's an external resources) 5337748cd8SNickeau */ 5437748cd8SNickeau private $content; 5537748cd8SNickeau /** 5637748cd8SNickeau * @var array 5737748cd8SNickeau */ 5837748cd8SNickeau private $headsTags; 5937748cd8SNickeau 6037748cd8SNickeau /** 6137748cd8SNickeau * Snippet constructor. 6237748cd8SNickeau */ 6337748cd8SNickeau public function __construct($snippetId, $snippetType) 6437748cd8SNickeau { 6537748cd8SNickeau $this->snippetId = $snippetId; 6637748cd8SNickeau $this->type = $snippetType; 6737748cd8SNickeau } 6837748cd8SNickeau 69*c3437056SNickeau public static function createJavascriptSnippet($snippetId): Snippet 7037748cd8SNickeau { 7137748cd8SNickeau return new Snippet($snippetId, self::TYPE_JS); 7237748cd8SNickeau } 7337748cd8SNickeau 74*c3437056SNickeau public static function createCssSnippet($snippetId): Snippet 7537748cd8SNickeau { 7637748cd8SNickeau return new Snippet($snippetId, self::TYPE_CSS); 7737748cd8SNickeau } 7837748cd8SNickeau 7937748cd8SNickeau /** 8037748cd8SNickeau * @param $snippetId 8137748cd8SNickeau * @return Snippet 82*c3437056SNickeau * @deprecated You should create a snippet with a known type, this constructor was created for refactoring 8337748cd8SNickeau */ 84*c3437056SNickeau public static function createUnknownSnippet($snippetId): Snippet 8537748cd8SNickeau { 8637748cd8SNickeau return new Snippet($snippetId, "unknwon"); 8737748cd8SNickeau } 8837748cd8SNickeau 8937748cd8SNickeau 9037748cd8SNickeau /** 9137748cd8SNickeau * @param $bool - if the snippet is critical, it would not be deferred or preloaded 9237748cd8SNickeau * @return Snippet for chaining 9337748cd8SNickeau * All css that are for animation or background for instance 9437748cd8SNickeau * should not be set as critical as they are not needed to paint 9537748cd8SNickeau * exactly the page 9637748cd8SNickeau */ 97*c3437056SNickeau public function setCritical($bool): Snippet 9837748cd8SNickeau { 9937748cd8SNickeau $this->critical = $bool; 10037748cd8SNickeau return $this; 10137748cd8SNickeau } 10237748cd8SNickeau 10337748cd8SNickeau /** 10437748cd8SNickeau * @param $content - Set an inline content for a script or stylesheet 10537748cd8SNickeau * @return Snippet for chaining 10637748cd8SNickeau */ 107*c3437056SNickeau public function setContent($content): Snippet 10837748cd8SNickeau { 10937748cd8SNickeau $this->content = $content; 11037748cd8SNickeau return $this; 11137748cd8SNickeau } 11237748cd8SNickeau 11337748cd8SNickeau /** 11437748cd8SNickeau * @return string 11537748cd8SNickeau */ 11637748cd8SNickeau public function getContent() 11737748cd8SNickeau { 11837748cd8SNickeau if ($this->content == null) { 11937748cd8SNickeau switch ($this->type) { 12037748cd8SNickeau case self::TYPE_CSS: 12137748cd8SNickeau $this->content = $this->getCssRulesFromFile($this->snippetId); 12237748cd8SNickeau break; 12337748cd8SNickeau case self::TYPE_JS: 12437748cd8SNickeau $this->content = $this->getJavascriptContentFromFile($this->snippetId); 12537748cd8SNickeau break; 12637748cd8SNickeau default: 12737748cd8SNickeau LogUtility::msg("The snippet ($this) has no content", LogUtility::LVL_MSG_ERROR, "support"); 12837748cd8SNickeau } 12937748cd8SNickeau } 13037748cd8SNickeau return $this->content; 13137748cd8SNickeau } 13237748cd8SNickeau 13337748cd8SNickeau /** 13437748cd8SNickeau * @param $tagName 13537748cd8SNickeau * @return false|string - the css content of the css file 13637748cd8SNickeau */ 13737748cd8SNickeau private function getCssRulesFromFile($tagName) 13837748cd8SNickeau { 13937748cd8SNickeau 14037748cd8SNickeau $path = Resources::getSnippetResourceDirectory() . "/style/" . strtolower($tagName) . ".css"; 14137748cd8SNickeau if (file_exists($path)) { 14237748cd8SNickeau return file_get_contents($path); 14337748cd8SNickeau } else { 14437748cd8SNickeau LogUtility::msg("The css file ($path) was not found", LogUtility::LVL_MSG_WARNING, $tagName); 14537748cd8SNickeau return ""; 14637748cd8SNickeau } 14737748cd8SNickeau 14837748cd8SNickeau } 14937748cd8SNickeau 15037748cd8SNickeau /** 15137748cd8SNickeau * @param $tagName - the tag name 15237748cd8SNickeau * @return false|string - the specific javascript content for the tag 15337748cd8SNickeau */ 15437748cd8SNickeau private function getJavascriptContentFromFile($tagName) 15537748cd8SNickeau { 15637748cd8SNickeau 15737748cd8SNickeau $path = Resources::getSnippetResourceDirectory() . "/js/" . strtolower($tagName) . ".js"; 15837748cd8SNickeau if (file_exists($path)) { 15937748cd8SNickeau return file_get_contents($path); 16037748cd8SNickeau } else { 16137748cd8SNickeau LogUtility::msg("The javascript file ($path) was not found", LogUtility::LVL_MSG_WARNING, $tagName); 16237748cd8SNickeau return ""; 16337748cd8SNickeau } 16437748cd8SNickeau 16537748cd8SNickeau } 16637748cd8SNickeau 16737748cd8SNickeau public function __toString() 16837748cd8SNickeau { 16937748cd8SNickeau return $this->snippetId . "-" . $this->type; 17037748cd8SNickeau } 17137748cd8SNickeau 17237748cd8SNickeau /** 17337748cd8SNickeau * Set all tags at once. 17437748cd8SNickeau * @param array $tags 17537748cd8SNickeau * @return Snippet 17637748cd8SNickeau */ 177*c3437056SNickeau public function setTags(array $tags): Snippet 17837748cd8SNickeau { 17937748cd8SNickeau $this->headsTags = $tags; 18037748cd8SNickeau return $this; 18137748cd8SNickeau } 18237748cd8SNickeau 183*c3437056SNickeau public function getTags(): array 18437748cd8SNickeau { 18537748cd8SNickeau return $this->headsTags; 18637748cd8SNickeau } 18737748cd8SNickeau 188*c3437056SNickeau public function getCritical(): bool 18937748cd8SNickeau { 190*c3437056SNickeau 191*c3437056SNickeau if ($this->critical === null) { 192*c3437056SNickeau if ($this->type == self::TYPE_CSS) { 193*c3437056SNickeau // All CSS should be loaded first 194*c3437056SNickeau // The CSS animation / background can set this to false 195*c3437056SNickeau return true; 196*c3437056SNickeau } 197*c3437056SNickeau return false; 198*c3437056SNickeau } 19937748cd8SNickeau return $this->critical; 20037748cd8SNickeau } 20137748cd8SNickeau 202*c3437056SNickeau public function getClass(): string 20337748cd8SNickeau { 20437748cd8SNickeau /** 20537748cd8SNickeau * The class for the snippet is just to be able to identify them 20637748cd8SNickeau * 20737748cd8SNickeau * The `snippet` prefix was added to be sure that the class 20837748cd8SNickeau * name will not conflict with a css class 20937748cd8SNickeau * Example: if you set the class to `combo-list` 21037748cd8SNickeau * and that you use it in a inline `style` tag with 21137748cd8SNickeau * the same class name, the inline `style` tag is not applied 21237748cd8SNickeau * 21337748cd8SNickeau */ 21437748cd8SNickeau return "snippet-" . $this->snippetId . "-" . SnippetManager::COMBO_CLASS_SUFFIX; 21537748cd8SNickeau 21637748cd8SNickeau } 21737748cd8SNickeau 21837748cd8SNickeau /** 21937748cd8SNickeau * @return string the HTML of the tag (works for now only with CSS content) 22037748cd8SNickeau */ 221*c3437056SNickeau public function getHtmlStyleTag(): string 22237748cd8SNickeau { 22337748cd8SNickeau $content = $this->getContent(); 22437748cd8SNickeau $class = $this->getClass(); 22537748cd8SNickeau return <<<EOF 22637748cd8SNickeau<style class="$class"> 22737748cd8SNickeau$content 22837748cd8SNickeau</style> 22937748cd8SNickeauEOF; 23037748cd8SNickeau 23137748cd8SNickeau } 23237748cd8SNickeau 23337748cd8SNickeau public function getId() 23437748cd8SNickeau { 23537748cd8SNickeau return $this->snippetId; 23637748cd8SNickeau } 23737748cd8SNickeau 23837748cd8SNickeau 239*c3437056SNickeau public function jsonSerialize(): array 240*c3437056SNickeau { 241*c3437056SNickeau $dataToSerialize = [ 242*c3437056SNickeau self::JSON_SNIPPET_ID_PROPERTY => $this->snippetId, 243*c3437056SNickeau self::JSON_TYPE_PROPERTY => $this->type 244*c3437056SNickeau ]; 245*c3437056SNickeau if ($this->critical !== null) { 246*c3437056SNickeau $dataToSerialize[self::JSON_CRITICAL_PROPERTY] = $this->critical; 247*c3437056SNickeau } 248*c3437056SNickeau if ($this->content !== null) { 249*c3437056SNickeau $dataToSerialize[self::JSON_CONTENT_PROPERTY] = $this->content; 250*c3437056SNickeau } 251*c3437056SNickeau if ($this->headsTags !== null) { 252*c3437056SNickeau $dataToSerialize[self::JSON_HEAD_PROPERTY] = $this->headsTags; 253*c3437056SNickeau } 254*c3437056SNickeau return $dataToSerialize; 255*c3437056SNickeau 256*c3437056SNickeau } 257*c3437056SNickeau 258*c3437056SNickeau /** 259*c3437056SNickeau * @throws ExceptionCombo 260*c3437056SNickeau */ 261*c3437056SNickeau public static function createFromJson($array): Snippet 262*c3437056SNickeau { 263*c3437056SNickeau $snippetId = $array[self::JSON_SNIPPET_ID_PROPERTY]; 264*c3437056SNickeau if ($snippetId === null) { 265*c3437056SNickeau throw new ExceptionCombo("The snippet id property was not found in the json array"); 266*c3437056SNickeau } 267*c3437056SNickeau $type = $array[self::JSON_TYPE_PROPERTY]; 268*c3437056SNickeau if ($type === null) { 269*c3437056SNickeau throw new ExceptionCombo("The snippet type property was not found in the json array"); 270*c3437056SNickeau } 271*c3437056SNickeau $snippet = new Snippet($snippetId, $type); 272*c3437056SNickeau $critical = $array[self::JSON_CRITICAL_PROPERTY]; 273*c3437056SNickeau if ($critical !== null) { 274*c3437056SNickeau $snippet->setCritical($critical); 275*c3437056SNickeau } 276*c3437056SNickeau 277*c3437056SNickeau $content = $array[self::JSON_CONTENT_PROPERTY]; 278*c3437056SNickeau if ($content !== null) { 279*c3437056SNickeau $snippet->setContent($content); 280*c3437056SNickeau } 281*c3437056SNickeau 282*c3437056SNickeau $heads = $array[self::JSON_HEAD_PROPERTY]; 283*c3437056SNickeau if ($heads !== null) { 284*c3437056SNickeau $snippet->setTags($heads); 285*c3437056SNickeau } 286*c3437056SNickeau return $snippet; 287*c3437056SNickeau 288*c3437056SNickeau } 28937748cd8SNickeau} 290