1<?php
2namespace TYPO3\CMS\Core\Utility;
3
4/*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17/**
18 * Class with helper functions for string handling
19 */
20class StringUtility
21{
22    /**
23     * Returns TRUE if $haystack begins with $needle.
24     * The input string is not trimmed before and search is done case sensitive.
25     *
26     * @param string $haystack Full string to check
27     * @param string $needle Reference string which must be found as the "first part" of the full string
28     * @throws \InvalidArgumentException
29     * @return bool TRUE if $needle was found to be equal to the first part of $haystack
30     */
31    public static function beginsWith($haystack, $needle)
32    {
33        // Sanitize $haystack and $needle
34        if (is_array($haystack) || is_object($haystack) || $haystack === null || (string)$haystack != $haystack) {
35            throw new \InvalidArgumentException(
36                '$haystack can not be interpreted as string',
37                1347135546
38            );
39        }
40        if (is_array($needle) || is_object($needle) || (string)$needle != $needle || strlen($needle) < 1) {
41            throw new \InvalidArgumentException(
42                '$needle can not be interpreted as string or has zero length',
43                1347135547
44            );
45        }
46        $haystack = (string)$haystack;
47        $needle = (string)$needle;
48        return $needle !== '' && strpos($haystack, $needle) === 0;
49    }
50
51    /**
52     * Returns TRUE if $haystack ends with $needle.
53     * The input string is not trimmed before and search is done case sensitive.
54     *
55     * @param string $haystack Full string to check
56     * @param string $needle Reference string which must be found as the "last part" of the full string
57     * @throws \InvalidArgumentException
58     * @return bool TRUE if $needle was found to be equal to the last part of $haystack
59     */
60    public static function endsWith($haystack, $needle)
61    {
62        // Sanitize $haystack and $needle
63        if (is_array($haystack) || is_object($haystack) || $haystack === null || (string)$haystack != $haystack) {
64            throw new \InvalidArgumentException(
65                '$haystack can not be interpreted as string',
66                1347135544
67            );
68        }
69        if (is_array($needle) || is_object($needle) || (string)$needle != $needle || strlen($needle) < 1) {
70            throw new \InvalidArgumentException(
71                '$needle can not be interpreted as string or has no length',
72                1347135545
73            );
74        }
75        $haystackLength = strlen($haystack);
76        $needleLength = strlen($needle);
77        if (!$haystackLength || $needleLength > $haystackLength) {
78            return false;
79        }
80        $position = strrpos((string)$haystack, (string)$needle);
81        return $position !== false && $position === $haystackLength - $needleLength;
82    }
83
84    /**
85     * This function generates a unique id by using the more entropy parameter.
86     * Furthermore the dots are removed so the id can be used inside HTML attributes e.g. id.
87     *
88     * @param string $prefix
89     * @return string
90     */
91    public static function getUniqueId($prefix = '')
92    {
93        $uniqueId = uniqid($prefix, true);
94        return str_replace('.', '', $uniqueId);
95    }
96
97    /**
98     * Escape a CSS selector to be used for DOM queries
99     *
100     * This method takes care to escape any CSS selector meta character.
101     * The result may be used to query the DOM like $('#' + escapedSelector)
102     *
103     * @param string $selector
104     * @return string
105     */
106    public static function escapeCssSelector(string $selector): string
107    {
108        return preg_replace('/([#:.\\[\\],=@])/', '\\\\$1', $selector);
109    }
110
111    /**
112     * Removes the Byte Order Mark (BOM) from the input string. This method supports UTF-8 encoded strings only!
113     *
114     * @param string $input
115     * @return string
116     */
117    public static function removeByteOrderMark(string $input): string
118    {
119        if (strpos($input, "\xef\xbb\xbf") === 0) {
120            $input = substr($input, 3);
121        }
122
123        return $input;
124    }
125
126    /**
127     * Matching two strings against each other, supporting a "*" wildcard (match many) or a "?" wildcard (match one= or (if wrapped in "/") PCRE regular expressions
128     *
129     * @param string $haystack The string in which to find $needle.
130     * @param string $needle The string to find in $haystack
131     * @return bool Returns TRUE if $needle matches or is found in (according to wildcards) $haystack. E.g. if $haystack is "Netscape 6.5" and $needle is "Net*" or "Net*ape" then it returns TRUE.
132     */
133    public static function searchStringWildcard($haystack, $needle): bool
134    {
135        $result = false;
136        if ($haystack === $needle) {
137            $result = true;
138        } elseif ($needle) {
139            if (preg_match('/^\\/.+\\/$/', $needle)) {
140                // Regular expression, only "//" is allowed as delimiter
141                $regex = $needle;
142            } else {
143                $needle = str_replace(['*', '?'], ['%%%MANY%%%', '%%%ONE%%%'], $needle);
144                $regex = '/^' . preg_quote($needle, '/') . '$/';
145                // Replace the marker with .* to match anything (wildcard)
146                $regex = str_replace(['%%%MANY%%%', '%%%ONE%%%'], ['.*', '.'], $regex);
147            }
148            $result = (bool)preg_match($regex, $haystack);
149        }
150        return $result;
151    }
152}
153