xref: /plugin/combo/ComboStrap/Html.php (revision 70bbd7f1f72440223cc13f3495efdcb2b0a11514)
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
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    }
4704fd306cSNickeau
4804fd306cSNickeau    /**
4904fd306cSNickeau     * Encode special HTML characters to entity (ie escaping)
5004fd306cSNickeau     *
5104fd306cSNickeau     * This is used to transform text that may be interpreted as HTML
5204fd306cSNickeau     * into a text
5304fd306cSNickeau     *   * that will not be interpreted as HTML
5404fd306cSNickeau     *   * that may be added in html attribute
5504fd306cSNickeau     *
5604fd306cSNickeau     * For instance:
5704fd306cSNickeau     *  * text that should go in attribute with special HTML characters (such as title)
5804fd306cSNickeau     *  * text that we don't create (to prevent HTML injection)
5904fd306cSNickeau     *
6004fd306cSNickeau     * Example:
6104fd306cSNickeau     *
6204fd306cSNickeau     * <script>...</script>
6304fd306cSNickeau     * to
6404fd306cSNickeau     * "&lt;script&gt;...&lt;/hello&gt;"
6504fd306cSNickeau     *
6604fd306cSNickeau     *
6704fd306cSNickeau     * @param $text
6804fd306cSNickeau     * @return string
6904fd306cSNickeau     *
7004fd306cSNickeau     * Note that if the `meta[charset]` matches the text encoding   , it should not be encoded
7104fd306cSNickeau     *
7204fd306cSNickeau     * True ? Beware that this still allows users to insert unsafe scripting vectors, such as markdown links like [xss](javascript:alert%281%29).
7304fd306cSNickeau     */
7404fd306cSNickeau    public static function encode($text): string
7504fd306cSNickeau    {
7604fd306cSNickeau        /**
7704fd306cSNickeau         * See https://stackoverflow.com/questions/46483/htmlentities-vs-htmlspecialchars/3614344
7804fd306cSNickeau         *
7904fd306cSNickeau         * Not {@link htmlentities } htmlentities($text, ENT_QUOTES);
8004fd306cSNickeau         * Otherwise we get `Error while loading HTMLError: Entity 'hellip' not defined`
8104fd306cSNickeau         * when loading HTML with {@link XmlDocument}
8204fd306cSNickeau         *
8304fd306cSNickeau         * See also {@link self::htmlDecode()}
8404fd306cSNickeau         *
8504fd306cSNickeau         * Without ENT_QUOTES
8604fd306cSNickeau         * <h4 class="heading-combo">
8704fd306cSNickeau         * is encoded as
8804fd306cSNickeau         * &gt;h4 class="heading-combo"&lt;
8904fd306cSNickeau         * and cannot be added in a attribute because of the quote
9004fd306cSNickeau         * This is used for {@link Tooltip}
9104fd306cSNickeau         */
9204fd306cSNickeau        return htmlspecialchars($text, ENT_XHTML | ENT_QUOTES | ENT_DISALLOWED);
9304fd306cSNickeau
9404fd306cSNickeau    }
9504fd306cSNickeau
9604fd306cSNickeau    public static function decode($int): string
9704fd306cSNickeau    {
9804fd306cSNickeau        return htmlspecialchars_decode($int, ENT_XHTML | ENT_QUOTES);
9904fd306cSNickeau    }
10004fd306cSNickeau
10104fd306cSNickeau    public static function getDiffBetweenValuesSeparatedByBlank(string $expected, string $actual, string $expectedName = "expected class", string $actualName = "actual class"): string
10204fd306cSNickeau    {
10304fd306cSNickeau        $leftClasses = preg_split("/\s/", $expected);
10404fd306cSNickeau        $rightClasses = preg_split("/\s/", $actual);
10504fd306cSNickeau        $error = "";
10604fd306cSNickeau        foreach ($leftClasses as $leftClass) {
10704fd306cSNickeau            if (!in_array($leftClass, $rightClasses)) {
10804fd306cSNickeau                $error .= "The $expectedName has the value (" . $leftClass . ") that is not present in the $actualName)\n";
10904fd306cSNickeau            } else {
11004fd306cSNickeau                // Delete the value
11104fd306cSNickeau                $key = array_search($leftClass, $rightClasses);
11204fd306cSNickeau                unset($rightClasses[$key]);
11304fd306cSNickeau            }
11404fd306cSNickeau        }
11504fd306cSNickeau        foreach ($rightClasses as $rightClass) {
11604fd306cSNickeau            $error .= "The $actualName has the value (" . $rightClass . ") that is not present in the $expectedName)\n";
11704fd306cSNickeau        }
11804fd306cSNickeau        return $error;
11904fd306cSNickeau    }
12004fd306cSNickeau
12104fd306cSNickeau    /**
12204fd306cSNickeau     * @throws ExceptionBadSyntax - bad url
12304fd306cSNickeau     * @throws ExceptionNotEquals - not equals
12404fd306cSNickeau     */
12504fd306cSNickeau    public static function getDiffBetweenSrcSet(string $expected, string $actual)
12604fd306cSNickeau    {
12704fd306cSNickeau        $expectedSrcSets = explode(",", $expected);
12804fd306cSNickeau        $actualSrcSets = explode(",", $actual);
12904fd306cSNickeau        $countExpected = count($expectedSrcSets);
13004fd306cSNickeau        $countActual = count($actualSrcSets);
13104fd306cSNickeau        if ($countExpected !== $countActual) {
13204fd306cSNickeau            throw new ExceptionNotEquals("The expected srcSet count ($countExpected) is not the same than the actual ($countActual).");
13304fd306cSNickeau        }
13404fd306cSNickeau        for ($i = 0; $i < $countExpected; $i++) {
13504fd306cSNickeau            $expectedSrcSet = trim($expectedSrcSets[$i]);
136*70bbd7f1Sgerardnico            $expectedSrcSetExploded = explode(" ", $expectedSrcSet, 2);
137*70bbd7f1Sgerardnico            $expectedSrc = $expectedSrcSetExploded[0];
138*70bbd7f1Sgerardnico            if (count($expectedSrcSetExploded) == 2) {
139*70bbd7f1Sgerardnico                $expectedWidth = $expectedSrcSetExploded[1];
140*70bbd7f1Sgerardnico            } else {
141*70bbd7f1Sgerardnico                $expectedWidth = null;
142*70bbd7f1Sgerardnico            }
14304fd306cSNickeau            $actualSrcSet = trim($actualSrcSets[$i]);
144*70bbd7f1Sgerardnico            $actualSrcSetExploded = explode(" ", $actualSrcSet, 2);
145*70bbd7f1Sgerardnico            $actualSrc = $actualSrcSetExploded[0];
146*70bbd7f1Sgerardnico            if (count($actualSrcSetExploded) == 2) {
147*70bbd7f1Sgerardnico                $actualWidth = $actualSrcSetExploded[1];
148*70bbd7f1Sgerardnico            } else {
149*70bbd7f1Sgerardnico                $actualWidth = null;
150*70bbd7f1Sgerardnico            }
15104fd306cSNickeau            if ($expectedWidth !== $actualWidth) {
15204fd306cSNickeau                throw new ExceptionNotEquals("The expected width ($expectedWidth) of the srcSet ($i) is not the same than the actual ($actualWidth).");
15304fd306cSNickeau            }
15404fd306cSNickeau            try {
15504fd306cSNickeau                Html::getDiffBetweenUrlStrings($expectedSrc, $actualSrc);
15604fd306cSNickeau            } catch (ExceptionBadSyntax $e) {
15704fd306cSNickeau                throw new ExceptionBadSyntax("Bad Syntax on Src Set ($i). Error: {$e->getMessage()}. Expected: $expectedSrc vs Actual: $actualSrc. ");
15804fd306cSNickeau            } catch (ExceptionNotEquals $e) {
15904fd306cSNickeau                throw ExceptionNotEquals::create("Not Equals on Src Set ($i). Error: {$e->getMessage()}.", $expectedSrc, $actualSrc);
16004fd306cSNickeau            }
16104fd306cSNickeau        }
16204fd306cSNickeau    }
16304fd306cSNickeau
16404fd306cSNickeau
16504fd306cSNickeau    /**
16604fd306cSNickeau     * @throws ExceptionBadSyntax
16704fd306cSNickeau     * @throws ExceptionNotEquals
16804fd306cSNickeau     */
16904fd306cSNickeau    public static function getDiffBetweenUrlStrings(string $expected, string $actual)
17004fd306cSNickeau    {
17104fd306cSNickeau        try {
17204fd306cSNickeau            $url = Url::createFromString($expected);
17304fd306cSNickeau        } catch (ExceptionBadSyntax $e) {
17404fd306cSNickeau            throw new ExceptionBadSyntax("The expected URL string ($expected) is not valid. Error: {$e->getMessage()}");
17504fd306cSNickeau        }
17604fd306cSNickeau        try {
17704fd306cSNickeau            $urlActual = Url::createFromString($actual);
17804fd306cSNickeau        } catch (ExceptionBadSyntax $e) {
17904fd306cSNickeau            throw new ExceptionBadSyntax("The $actual URL string ($actual) is not valid. Error: {$e->getMessage()}");
18004fd306cSNickeau        }
18104fd306cSNickeau        $url->equals($urlActual);
18204fd306cSNickeau
18304fd306cSNickeau    }
18404fd306cSNickeau
18504fd306cSNickeau    /**
18604fd306cSNickeau     * Merge class name
18704fd306cSNickeau     * @param string $newNames - the name that we want to add
18804fd306cSNickeau     * @param ?string $actualNames - the actual names
18904fd306cSNickeau     * @return string - the class name list
19004fd306cSNickeau     *
19104fd306cSNickeau     * for instance:
19204fd306cSNickeau     *   * newNames = foo blue
19304fd306cSNickeau     *   * actual Name = foo bar
19404fd306cSNickeau     * return
19504fd306cSNickeau     *   * foo bar blue
19604fd306cSNickeau     */
19704fd306cSNickeau    public static function mergeClassNames(string $newNames, ?string $actualNames): string
19804fd306cSNickeau    {
19904fd306cSNickeau        /**
20004fd306cSNickeau         * It may be in the form "value1 value2"
20104fd306cSNickeau         */
20204fd306cSNickeau        $newValues = StringUtility::explodeAndTrim($newNames, " ");
20304fd306cSNickeau        if (!empty($actualNames)) {
20404fd306cSNickeau            $actualValues = StringUtility::explodeAndTrim(trim($actualNames), " ");
20504fd306cSNickeau        } else {
20604fd306cSNickeau            $actualValues = [];
20704fd306cSNickeau        }
20804fd306cSNickeau        $newValues = array_merge($actualValues, $newValues);
20904fd306cSNickeau        $newValues = array_unique($newValues);
21004fd306cSNickeau        return implode(" ", $newValues);
21104fd306cSNickeau    }
21204fd306cSNickeau
21304fd306cSNickeau    /**
21404fd306cSNickeau     * @param array $styleProperties - an array of CSS properties with key, value
21504fd306cSNickeau     * @return string - the value for the style attribute (ie all rules where joined with the comma)
21604fd306cSNickeau     */
21704fd306cSNickeau    public static function array2InlineStyle(array $styleProperties)
21804fd306cSNickeau    {
21904fd306cSNickeau        $inlineCss = "";
22004fd306cSNickeau        foreach ($styleProperties as $key => $value) {
22104fd306cSNickeau            $inlineCss .= "$key:$value;";
22204fd306cSNickeau        }
22304fd306cSNickeau        // Suppress the last ;
22404fd306cSNickeau        if ($inlineCss[strlen($inlineCss) - 1] == ";") {
22504fd306cSNickeau            $inlineCss = substr($inlineCss, 0, -1);
22604fd306cSNickeau        }
22704fd306cSNickeau        return $inlineCss;
22804fd306cSNickeau    }
229c3437056SNickeau}
230