1c3437056SNickeau<?php 2c3437056SNickeau 3c3437056SNickeau 4c3437056SNickeaunamespace ComboStrap; 5c3437056SNickeau 6c3437056SNickeau 704fd306cSNickeauuse ComboStrap\Web\Url; 804fd306cSNickeauuse ComboStrap\Xml\XmlDocument; 904fd306cSNickeau 10c3437056SNickeauclass Html 11c3437056SNickeau{ 12c3437056SNickeau 13c3437056SNickeau 14c3437056SNickeau /** 15c3437056SNickeau * @param string $name 1604fd306cSNickeau * @throws ExceptionRuntime 17c3437056SNickeau * Garbage In / Garbage Out design 18c3437056SNickeau */ 19c3437056SNickeau public static function validNameGuard(string $name) 20c3437056SNickeau { 21c3437056SNickeau /** 22c3437056SNickeau * If the name is not in lowercase, 23c3437056SNickeau * the shorthand css selector does not work 24c3437056SNickeau */ 25c3437056SNickeau $validName = strtolower($name); 26c3437056SNickeau if ($validName != $name) { 2704fd306cSNickeau throw new ExceptionRuntime("The name ($name) is not a valid name"); 28c3437056SNickeau } 29c3437056SNickeau } 304cadd4f8SNickeau 314cadd4f8SNickeau /** 324cadd4f8SNickeau * Transform a text into a valid HTML id 334cadd4f8SNickeau * @param $string 344cadd4f8SNickeau * @return string 35*9154f6e3Sgerardnico * 36*9154f6e3Sgerardnico * See also: https://github.com/Flet/github-slugger 374cadd4f8SNickeau */ 384cadd4f8SNickeau public static function toHtmlId($string): string 394cadd4f8SNickeau { 404cadd4f8SNickeau /** 414cadd4f8SNickeau * sectionId calls cleanID 424cadd4f8SNickeau * cleanID delete all things before a ':' 434cadd4f8SNickeau * we do then the replace before to not 444cadd4f8SNickeau * lost a minus '-' separator 454cadd4f8SNickeau */ 464cadd4f8SNickeau $string = str_replace(array(':', '.'), '', $string); 474cadd4f8SNickeau return sectionID($string, $check); 484cadd4f8SNickeau } 4904fd306cSNickeau 5004fd306cSNickeau /** 5104fd306cSNickeau * Encode special HTML characters to entity (ie escaping) 5204fd306cSNickeau * 5304fd306cSNickeau * This is used to transform text that may be interpreted as HTML 5404fd306cSNickeau * into a text 5504fd306cSNickeau * * that will not be interpreted as HTML 5604fd306cSNickeau * * that may be added in html attribute 5704fd306cSNickeau * 5804fd306cSNickeau * For instance: 5904fd306cSNickeau * * text that should go in attribute with special HTML characters (such as title) 6004fd306cSNickeau * * text that we don't create (to prevent HTML injection) 6104fd306cSNickeau * 6204fd306cSNickeau * Example: 6304fd306cSNickeau * 6404fd306cSNickeau * <script>...</script> 6504fd306cSNickeau * to 6604fd306cSNickeau * "<script>...</hello>" 6704fd306cSNickeau * 6804fd306cSNickeau * 6904fd306cSNickeau * @param $text 7004fd306cSNickeau * @return string 7104fd306cSNickeau * 7204fd306cSNickeau * Note that if the `meta[charset]` matches the text encoding , it should not be encoded 7304fd306cSNickeau * 7404fd306cSNickeau * True ? Beware that this still allows users to insert unsafe scripting vectors, such as markdown links like [xss](javascript:alert%281%29). 7504fd306cSNickeau */ 7604fd306cSNickeau public static function encode($text): string 7704fd306cSNickeau { 7804fd306cSNickeau /** 7904fd306cSNickeau * See https://stackoverflow.com/questions/46483/htmlentities-vs-htmlspecialchars/3614344 8004fd306cSNickeau * 8104fd306cSNickeau * Not {@link htmlentities } htmlentities($text, ENT_QUOTES); 8204fd306cSNickeau * Otherwise we get `Error while loading HTMLError: Entity 'hellip' not defined` 8304fd306cSNickeau * when loading HTML with {@link XmlDocument} 8404fd306cSNickeau * 8504fd306cSNickeau * See also {@link self::htmlDecode()} 8604fd306cSNickeau * 8704fd306cSNickeau * Without ENT_QUOTES 8804fd306cSNickeau * <h4 class="heading-combo"> 8904fd306cSNickeau * is encoded as 9004fd306cSNickeau * >h4 class="heading-combo"< 9104fd306cSNickeau * and cannot be added in a attribute because of the quote 9204fd306cSNickeau * This is used for {@link Tooltip} 9304fd306cSNickeau */ 9404fd306cSNickeau return htmlspecialchars($text, ENT_XHTML | ENT_QUOTES | ENT_DISALLOWED); 9504fd306cSNickeau 9604fd306cSNickeau } 9704fd306cSNickeau 9804fd306cSNickeau public static function decode($int): string 9904fd306cSNickeau { 10004fd306cSNickeau return htmlspecialchars_decode($int, ENT_XHTML | ENT_QUOTES); 10104fd306cSNickeau } 10204fd306cSNickeau 10304fd306cSNickeau public static function getDiffBetweenValuesSeparatedByBlank(string $expected, string $actual, string $expectedName = "expected class", string $actualName = "actual class"): string 10404fd306cSNickeau { 10504fd306cSNickeau $leftClasses = preg_split("/\s/", $expected); 10604fd306cSNickeau $rightClasses = preg_split("/\s/", $actual); 10704fd306cSNickeau $error = ""; 10804fd306cSNickeau foreach ($leftClasses as $leftClass) { 10904fd306cSNickeau if (!in_array($leftClass, $rightClasses)) { 11004fd306cSNickeau $error .= "The $expectedName has the value (" . $leftClass . ") that is not present in the $actualName)\n"; 11104fd306cSNickeau } else { 11204fd306cSNickeau // Delete the value 11304fd306cSNickeau $key = array_search($leftClass, $rightClasses); 11404fd306cSNickeau unset($rightClasses[$key]); 11504fd306cSNickeau } 11604fd306cSNickeau } 11704fd306cSNickeau foreach ($rightClasses as $rightClass) { 11804fd306cSNickeau $error .= "The $actualName has the value (" . $rightClass . ") that is not present in the $expectedName)\n"; 11904fd306cSNickeau } 12004fd306cSNickeau return $error; 12104fd306cSNickeau } 12204fd306cSNickeau 12304fd306cSNickeau /** 12404fd306cSNickeau * @throws ExceptionBadSyntax - bad url 12504fd306cSNickeau * @throws ExceptionNotEquals - not equals 12604fd306cSNickeau */ 12704fd306cSNickeau public static function getDiffBetweenSrcSet(string $expected, string $actual) 12804fd306cSNickeau { 12904fd306cSNickeau $expectedSrcSets = explode(",", $expected); 13004fd306cSNickeau $actualSrcSets = explode(",", $actual); 13104fd306cSNickeau $countExpected = count($expectedSrcSets); 13204fd306cSNickeau $countActual = count($actualSrcSets); 13304fd306cSNickeau if ($countExpected !== $countActual) { 13404fd306cSNickeau throw new ExceptionNotEquals("The expected srcSet count ($countExpected) is not the same than the actual ($countActual)."); 13504fd306cSNickeau } 13604fd306cSNickeau for ($i = 0; $i < $countExpected; $i++) { 13704fd306cSNickeau $expectedSrcSet = trim($expectedSrcSets[$i]); 13870bbd7f1Sgerardnico $expectedSrcSetExploded = explode(" ", $expectedSrcSet, 2); 13970bbd7f1Sgerardnico $expectedSrc = $expectedSrcSetExploded[0]; 14070bbd7f1Sgerardnico if (count($expectedSrcSetExploded) == 2) { 14170bbd7f1Sgerardnico $expectedWidth = $expectedSrcSetExploded[1]; 14270bbd7f1Sgerardnico } else { 14370bbd7f1Sgerardnico $expectedWidth = null; 14470bbd7f1Sgerardnico } 14504fd306cSNickeau $actualSrcSet = trim($actualSrcSets[$i]); 14670bbd7f1Sgerardnico $actualSrcSetExploded = explode(" ", $actualSrcSet, 2); 14770bbd7f1Sgerardnico $actualSrc = $actualSrcSetExploded[0]; 14870bbd7f1Sgerardnico if (count($actualSrcSetExploded) == 2) { 14970bbd7f1Sgerardnico $actualWidth = $actualSrcSetExploded[1]; 15070bbd7f1Sgerardnico } else { 15170bbd7f1Sgerardnico $actualWidth = null; 15270bbd7f1Sgerardnico } 15304fd306cSNickeau if ($expectedWidth !== $actualWidth) { 15404fd306cSNickeau throw new ExceptionNotEquals("The expected width ($expectedWidth) of the srcSet ($i) is not the same than the actual ($actualWidth)."); 15504fd306cSNickeau } 15604fd306cSNickeau try { 15704fd306cSNickeau Html::getDiffBetweenUrlStrings($expectedSrc, $actualSrc); 15804fd306cSNickeau } catch (ExceptionBadSyntax $e) { 15904fd306cSNickeau throw new ExceptionBadSyntax("Bad Syntax on Src Set ($i). Error: {$e->getMessage()}. Expected: $expectedSrc vs Actual: $actualSrc. "); 16004fd306cSNickeau } catch (ExceptionNotEquals $e) { 16104fd306cSNickeau throw ExceptionNotEquals::create("Not Equals on Src Set ($i). Error: {$e->getMessage()}.", $expectedSrc, $actualSrc); 16204fd306cSNickeau } 16304fd306cSNickeau } 16404fd306cSNickeau } 16504fd306cSNickeau 16604fd306cSNickeau 16704fd306cSNickeau /** 16804fd306cSNickeau * @throws ExceptionBadSyntax 16904fd306cSNickeau * @throws ExceptionNotEquals 17004fd306cSNickeau */ 17104fd306cSNickeau public static function getDiffBetweenUrlStrings(string $expected, string $actual) 17204fd306cSNickeau { 17304fd306cSNickeau try { 17404fd306cSNickeau $url = Url::createFromString($expected); 17504fd306cSNickeau } catch (ExceptionBadSyntax $e) { 17604fd306cSNickeau throw new ExceptionBadSyntax("The expected URL string ($expected) is not valid. Error: {$e->getMessage()}"); 17704fd306cSNickeau } 17804fd306cSNickeau try { 17904fd306cSNickeau $urlActual = Url::createFromString($actual); 18004fd306cSNickeau } catch (ExceptionBadSyntax $e) { 18104fd306cSNickeau throw new ExceptionBadSyntax("The $actual URL string ($actual) is not valid. Error: {$e->getMessage()}"); 18204fd306cSNickeau } 18304fd306cSNickeau $url->equals($urlActual); 18404fd306cSNickeau 18504fd306cSNickeau } 18604fd306cSNickeau 18704fd306cSNickeau /** 18804fd306cSNickeau * Merge class name 18904fd306cSNickeau * @param string $newNames - the name that we want to add 19004fd306cSNickeau * @param ?string $actualNames - the actual names 19104fd306cSNickeau * @return string - the class name list 19204fd306cSNickeau * 19304fd306cSNickeau * for instance: 19404fd306cSNickeau * * newNames = foo blue 19504fd306cSNickeau * * actual Name = foo bar 19604fd306cSNickeau * return 19704fd306cSNickeau * * foo bar blue 19804fd306cSNickeau */ 19904fd306cSNickeau public static function mergeClassNames(string $newNames, ?string $actualNames): string 20004fd306cSNickeau { 20104fd306cSNickeau /** 20204fd306cSNickeau * It may be in the form "value1 value2" 20304fd306cSNickeau */ 20404fd306cSNickeau $newValues = StringUtility::explodeAndTrim($newNames, " "); 20504fd306cSNickeau if (!empty($actualNames)) { 20604fd306cSNickeau $actualValues = StringUtility::explodeAndTrim(trim($actualNames), " "); 20704fd306cSNickeau } else { 20804fd306cSNickeau $actualValues = []; 20904fd306cSNickeau } 21004fd306cSNickeau $newValues = array_merge($actualValues, $newValues); 21104fd306cSNickeau $newValues = array_unique($newValues); 21204fd306cSNickeau return implode(" ", $newValues); 21304fd306cSNickeau } 21404fd306cSNickeau 21504fd306cSNickeau /** 21604fd306cSNickeau * @param array $styleProperties - an array of CSS properties with key, value 21704fd306cSNickeau * @return string - the value for the style attribute (ie all rules where joined with the comma) 21804fd306cSNickeau */ 21904fd306cSNickeau public static function array2InlineStyle(array $styleProperties) 22004fd306cSNickeau { 22104fd306cSNickeau $inlineCss = ""; 22204fd306cSNickeau foreach ($styleProperties as $key => $value) { 22304fd306cSNickeau $inlineCss .= "$key:$value;"; 22404fd306cSNickeau } 22504fd306cSNickeau // Suppress the last ; 22604fd306cSNickeau if ($inlineCss[strlen($inlineCss) - 1] == ";") { 22704fd306cSNickeau $inlineCss = substr($inlineCss, 0, -1); 22804fd306cSNickeau } 22904fd306cSNickeau return $inlineCss; 23004fd306cSNickeau } 231c3437056SNickeau} 232