1c3437056SNickeau<?php 2c3437056SNickeau 3c3437056SNickeau 4c3437056SNickeaunamespace ComboStrap; 5c3437056SNickeau 6c3437056SNickeau 7*04fd306cSNickeauuse ComboStrap\Web\Url; 8*04fd306cSNickeauuse ComboStrap\Xml\XmlDocument; 9*04fd306cSNickeau 10c3437056SNickeauclass Html 11c3437056SNickeau{ 12c3437056SNickeau 13c3437056SNickeau 14c3437056SNickeau /** 15c3437056SNickeau * @param string $name 16*04fd306cSNickeau * @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) { 27*04fd306cSNickeau 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 354cadd4f8SNickeau */ 364cadd4f8SNickeau public static function toHtmlId($string): string 374cadd4f8SNickeau { 384cadd4f8SNickeau /** 394cadd4f8SNickeau * sectionId calls cleanID 404cadd4f8SNickeau * cleanID delete all things before a ':' 414cadd4f8SNickeau * we do then the replace before to not 424cadd4f8SNickeau * lost a minus '-' separator 434cadd4f8SNickeau */ 444cadd4f8SNickeau $string = str_replace(array(':', '.'), '', $string); 454cadd4f8SNickeau return sectionID($string, $check); 464cadd4f8SNickeau } 47*04fd306cSNickeau 48*04fd306cSNickeau /** 49*04fd306cSNickeau * Encode special HTML characters to entity (ie escaping) 50*04fd306cSNickeau * 51*04fd306cSNickeau * This is used to transform text that may be interpreted as HTML 52*04fd306cSNickeau * into a text 53*04fd306cSNickeau * * that will not be interpreted as HTML 54*04fd306cSNickeau * * that may be added in html attribute 55*04fd306cSNickeau * 56*04fd306cSNickeau * For instance: 57*04fd306cSNickeau * * text that should go in attribute with special HTML characters (such as title) 58*04fd306cSNickeau * * text that we don't create (to prevent HTML injection) 59*04fd306cSNickeau * 60*04fd306cSNickeau * Example: 61*04fd306cSNickeau * 62*04fd306cSNickeau * <script>...</script> 63*04fd306cSNickeau * to 64*04fd306cSNickeau * "<script>...</hello>" 65*04fd306cSNickeau * 66*04fd306cSNickeau * 67*04fd306cSNickeau * @param $text 68*04fd306cSNickeau * @return string 69*04fd306cSNickeau * 70*04fd306cSNickeau * Note that if the `meta[charset]` matches the text encoding , it should not be encoded 71*04fd306cSNickeau * 72*04fd306cSNickeau * True ? Beware that this still allows users to insert unsafe scripting vectors, such as markdown links like [xss](javascript:alert%281%29). 73*04fd306cSNickeau */ 74*04fd306cSNickeau public static function encode($text): string 75*04fd306cSNickeau { 76*04fd306cSNickeau /** 77*04fd306cSNickeau * See https://stackoverflow.com/questions/46483/htmlentities-vs-htmlspecialchars/3614344 78*04fd306cSNickeau * 79*04fd306cSNickeau * Not {@link htmlentities } htmlentities($text, ENT_QUOTES); 80*04fd306cSNickeau * Otherwise we get `Error while loading HTMLError: Entity 'hellip' not defined` 81*04fd306cSNickeau * when loading HTML with {@link XmlDocument} 82*04fd306cSNickeau * 83*04fd306cSNickeau * See also {@link self::htmlDecode()} 84*04fd306cSNickeau * 85*04fd306cSNickeau * Without ENT_QUOTES 86*04fd306cSNickeau * <h4 class="heading-combo"> 87*04fd306cSNickeau * is encoded as 88*04fd306cSNickeau * >h4 class="heading-combo"< 89*04fd306cSNickeau * and cannot be added in a attribute because of the quote 90*04fd306cSNickeau * This is used for {@link Tooltip} 91*04fd306cSNickeau */ 92*04fd306cSNickeau return htmlspecialchars($text, ENT_XHTML | ENT_QUOTES | ENT_DISALLOWED); 93*04fd306cSNickeau 94*04fd306cSNickeau } 95*04fd306cSNickeau 96*04fd306cSNickeau public static function decode($int): string 97*04fd306cSNickeau { 98*04fd306cSNickeau return htmlspecialchars_decode($int, ENT_XHTML | ENT_QUOTES); 99*04fd306cSNickeau } 100*04fd306cSNickeau 101*04fd306cSNickeau public static function getDiffBetweenValuesSeparatedByBlank(string $expected, string $actual, string $expectedName = "expected class", string $actualName = "actual class"): string 102*04fd306cSNickeau { 103*04fd306cSNickeau $leftClasses = preg_split("/\s/", $expected); 104*04fd306cSNickeau $rightClasses = preg_split("/\s/", $actual); 105*04fd306cSNickeau $error = ""; 106*04fd306cSNickeau foreach ($leftClasses as $leftClass) { 107*04fd306cSNickeau if (!in_array($leftClass, $rightClasses)) { 108*04fd306cSNickeau $error .= "The $expectedName has the value (" . $leftClass . ") that is not present in the $actualName)\n"; 109*04fd306cSNickeau } else { 110*04fd306cSNickeau // Delete the value 111*04fd306cSNickeau $key = array_search($leftClass, $rightClasses); 112*04fd306cSNickeau unset($rightClasses[$key]); 113*04fd306cSNickeau } 114*04fd306cSNickeau } 115*04fd306cSNickeau foreach ($rightClasses as $rightClass) { 116*04fd306cSNickeau $error .= "The $actualName has the value (" . $rightClass . ") that is not present in the $expectedName)\n"; 117*04fd306cSNickeau } 118*04fd306cSNickeau return $error; 119*04fd306cSNickeau } 120*04fd306cSNickeau 121*04fd306cSNickeau /** 122*04fd306cSNickeau * @throws ExceptionBadSyntax - bad url 123*04fd306cSNickeau * @throws ExceptionNotEquals - not equals 124*04fd306cSNickeau */ 125*04fd306cSNickeau public static function getDiffBetweenSrcSet(string $expected, string $actual) 126*04fd306cSNickeau { 127*04fd306cSNickeau $expectedSrcSets = explode(",", $expected); 128*04fd306cSNickeau $actualSrcSets = explode(",", $actual); 129*04fd306cSNickeau $countExpected = count($expectedSrcSets); 130*04fd306cSNickeau $countActual = count($actualSrcSets); 131*04fd306cSNickeau if ($countExpected !== $countActual) { 132*04fd306cSNickeau throw new ExceptionNotEquals("The expected srcSet count ($countExpected) is not the same than the actual ($countActual)."); 133*04fd306cSNickeau } 134*04fd306cSNickeau for ($i = 0; $i < $countExpected; $i++) { 135*04fd306cSNickeau $expectedSrcSet = trim($expectedSrcSets[$i]); 136*04fd306cSNickeau [$expectedSrc, $expectedWidth] = explode(" ", $expectedSrcSet, 2); 137*04fd306cSNickeau $actualSrcSet = trim($actualSrcSets[$i]); 138*04fd306cSNickeau [$actualSrc, $actualWidth] = explode(" ", $actualSrcSet, 2); 139*04fd306cSNickeau if ($expectedWidth !== $actualWidth) { 140*04fd306cSNickeau throw new ExceptionNotEquals("The expected width ($expectedWidth) of the srcSet ($i) is not the same than the actual ($actualWidth)."); 141*04fd306cSNickeau } 142*04fd306cSNickeau try { 143*04fd306cSNickeau Html::getDiffBetweenUrlStrings($expectedSrc, $actualSrc); 144*04fd306cSNickeau } catch (ExceptionBadSyntax $e) { 145*04fd306cSNickeau throw new ExceptionBadSyntax("Bad Syntax on Src Set ($i). Error: {$e->getMessage()}. Expected: $expectedSrc vs Actual: $actualSrc. "); 146*04fd306cSNickeau } catch (ExceptionNotEquals $e) { 147*04fd306cSNickeau throw ExceptionNotEquals::create("Not Equals on Src Set ($i). Error: {$e->getMessage()}.", $expectedSrc, $actualSrc); 148*04fd306cSNickeau } 149*04fd306cSNickeau } 150*04fd306cSNickeau } 151*04fd306cSNickeau 152*04fd306cSNickeau 153*04fd306cSNickeau 154*04fd306cSNickeau /** 155*04fd306cSNickeau * @throws ExceptionBadSyntax 156*04fd306cSNickeau * @throws ExceptionNotEquals 157*04fd306cSNickeau */ 158*04fd306cSNickeau public static function getDiffBetweenUrlStrings(string $expected, string $actual) 159*04fd306cSNickeau { 160*04fd306cSNickeau try { 161*04fd306cSNickeau $url = Url::createFromString($expected); 162*04fd306cSNickeau } catch (ExceptionBadSyntax $e) { 163*04fd306cSNickeau throw new ExceptionBadSyntax("The expected URL string ($expected) is not valid. Error: {$e->getMessage()}"); 164*04fd306cSNickeau } 165*04fd306cSNickeau try { 166*04fd306cSNickeau $urlActual = Url::createFromString($actual); 167*04fd306cSNickeau } catch (ExceptionBadSyntax $e) { 168*04fd306cSNickeau throw new ExceptionBadSyntax("The $actual URL string ($actual) is not valid. Error: {$e->getMessage()}"); 169*04fd306cSNickeau } 170*04fd306cSNickeau $url->equals($urlActual); 171*04fd306cSNickeau 172*04fd306cSNickeau } 173*04fd306cSNickeau 174*04fd306cSNickeau /** 175*04fd306cSNickeau * Merge class name 176*04fd306cSNickeau * @param string $newNames - the name that we want to add 177*04fd306cSNickeau * @param ?string $actualNames - the actual names 178*04fd306cSNickeau * @return string - the class name list 179*04fd306cSNickeau * 180*04fd306cSNickeau * for instance: 181*04fd306cSNickeau * * newNames = foo blue 182*04fd306cSNickeau * * actual Name = foo bar 183*04fd306cSNickeau * return 184*04fd306cSNickeau * * foo bar blue 185*04fd306cSNickeau */ 186*04fd306cSNickeau public static function mergeClassNames(string $newNames, ?string $actualNames): string 187*04fd306cSNickeau { 188*04fd306cSNickeau /** 189*04fd306cSNickeau * It may be in the form "value1 value2" 190*04fd306cSNickeau */ 191*04fd306cSNickeau $newValues = StringUtility::explodeAndTrim($newNames, " "); 192*04fd306cSNickeau if (!empty($actualNames)) { 193*04fd306cSNickeau $actualValues = StringUtility::explodeAndTrim(trim($actualNames), " "); 194*04fd306cSNickeau } else { 195*04fd306cSNickeau $actualValues = []; 196*04fd306cSNickeau } 197*04fd306cSNickeau $newValues = array_merge($actualValues, $newValues); 198*04fd306cSNickeau $newValues = array_unique($newValues); 199*04fd306cSNickeau return implode(" ", $newValues); 200*04fd306cSNickeau } 201*04fd306cSNickeau 202*04fd306cSNickeau /** 203*04fd306cSNickeau * @param array $styleProperties - an array of CSS properties with key, value 204*04fd306cSNickeau * @return string - the value for the style attribute (ie all rules where joined with the comma) 205*04fd306cSNickeau */ 206*04fd306cSNickeau public static function array2InlineStyle(array $styleProperties) 207*04fd306cSNickeau { 208*04fd306cSNickeau $inlineCss = ""; 209*04fd306cSNickeau foreach ($styleProperties as $key => $value) { 210*04fd306cSNickeau $inlineCss .= "$key:$value;"; 211*04fd306cSNickeau } 212*04fd306cSNickeau // Suppress the last ; 213*04fd306cSNickeau if ($inlineCss[strlen($inlineCss) - 1] == ";") { 214*04fd306cSNickeau $inlineCss = substr($inlineCss, 0, -1); 215*04fd306cSNickeau } 216*04fd306cSNickeau return $inlineCss; 217*04fd306cSNickeau } 218c3437056SNickeau} 219