: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @license https://opensource.org/licenses/mit-license.php MIT License
use Cake\Core\Exception\Exception;
use InvalidArgumentException;
* Default transliterator.
* @var \Transliterator|null Transliterator instance.
protected static $_defaultTransliterator;
* Default transliterator id string.
* @var string $_defaultTransliteratorId Transliterator identifier string.
protected static $_defaultTransliteratorId = 'Any-Latin; Latin-ASCII; [\u0080-\u7fff] remove';
* Default html tags who must not be count for truncate text.
protected static $_defaultHtmlNoCount = [
* Generate a random UUID version 4
* Warning: This method should not be used as a random seed for any cryptographic operations.
* Instead you should use the openssl or mcrypt extensions.
* It should also not be used to create identifiers that have security implications, such as
* 'unguessable' URL identifiers. Instead you should use `Security::randomBytes()` for that.
* @see https://www.ietf.org/rfc/rfc4122.txt
* @return string RFC 4122 UUID
* @copyright Matt Farina MIT License https://github.com/lootils/uuid/blob/master/LICENSE
public static function uuid()
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 bits for "time_low"
// 16 bits for "time_mid"
// 12 bits before the 0100 of (version) 4 for "time_hi_and_version"
random_int(0, 4095) | 0x4000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
random_int(0, 0x3fff) | 0x8000,
* Tokenizes a string using $separator, ignoring any instance of $separator that appears between
* $leftBound and $rightBound.
* @param string $data The data to tokenize.
* @param string $separator The token to split the data on.
* @param string $leftBound The left boundary to ignore separators in.
* @param string $rightBound The right boundary to ignore separators in.
* @return string|string[] Array of tokens in $data or original input if empty.
public static function tokenize($data, $separator = ',', $leftBound = '(', $rightBound = ')')
$length = mb_strlen($data);
while ($offset <= $length) {
mb_strpos($data, $separator, $offset),
mb_strpos($data, $leftBound, $offset),
mb_strpos($data, $rightBound, $offset),
for ($i = 0; $i < 3; $i++) {
if ($offsets[$i] !== false && ($offsets[$i] < $tmpOffset || $tmpOffset == -1)) {
$tmpOffset = $offsets[$i];
$buffer .= mb_substr($data, $offset, $tmpOffset - $offset);
$char = mb_substr($data, $tmpOffset, 1);
if (!$depth && $char === $separator) {
if ($leftBound !== $rightBound) {
if ($char === $leftBound) {
if ($char === $rightBound) {
if ($char === $leftBound) {
$results[] = $buffer . mb_substr($data, $offset);
if (empty($results) && !empty($buffer)) {
return array_map('trim', $results);
* Replaces variable placeholders inside a $str with any given $data. Each key in the $data array
* corresponds to a variable placeholder name in $str.
* Text::insert(':name is :age years old.', ['name' => 'Bob', 'age' => '65']);
* Returns: Bob is 65 years old.
* Available $options are:
* - before: The character or string in front of the name of the variable placeholder (Defaults to `:`)
* - after: The character or string after the name of the variable placeholder (Defaults to null)
* - escape: The character or string used to escape the before character / string (Defaults to `\`)
* - format: A regex to use for matching variable placeholders. Default is: `/(?<!\\)\:%s/`
* (Overwrites before, after, breaks escape / clean)
* - clean: A boolean or array with instructions for Text::cleanInsert
* @param string $str A string containing variable placeholders
* @param array $data A key => val array where each key stands for a placeholder variable name
* to be replaced with val
* @param array $options An array of options, see description above
public static function insert($str, $data, array $options = [])
'before' => ':', 'after' => null, 'escape' => '\\', 'format' => null, 'clean' => false,
$format = $options['format'];
return $options['clean'] ? static::cleanInsert($str, $options) : $str;
preg_quote($options['escape'], '/'),
str_replace('%', '%%', preg_quote($options['before'], '/')),
str_replace('%', '%%', preg_quote($options['after'], '/'))
if (strpos($str, '?') !== false && is_numeric(key($data))) {
while (($pos = strpos($str, '?', $offset)) !== false) {
$val = array_shift($data);
$offset = $pos + strlen($val);
$str = substr_replace($str, $val, $pos, 1);
return $options['clean'] ? static::cleanInsert($str, $options) : $str;
$dataKeys = array_keys($data);
$hashKeys = array_map('crc32', $dataKeys);
$tempData = array_combine($dataKeys, $hashKeys);
foreach ($tempData as $key => $hashVal) {
$key = sprintf($format, preg_quote($key, '/'));
$str = preg_replace($key, $hashVal, $str);
$dataReplacements = array_combine($hashKeys, array_values($data));
foreach ($dataReplacements as $tmpHash => $tmpValue) {
$tmpValue = is_array($tmpValue) ? '' : $tmpValue;
$str = str_replace($tmpHash, $tmpValue, $str);
if (!isset($options['format']) && isset($options['before'])) {
$str = str_replace($options['escape'] . $options['before'], $options['before'], $str);
return $options['clean'] ? static::cleanInsert($str, $options) : $str;
* Cleans up a Text::insert() formatted string with given $options depending on the 'clean' key in
* $options. The default method used is text but html is also available. The goal of this function
* is to replace all whitespace and unneeded markup around placeholders that did not get replaced
* @param string $str String to clean.
* @param array $options Options list.
* @see \Cake\Utility\Text::insert()
public static function cleanInsert($str, array $options)
$clean = $options['clean'];
$clean = ['method' => 'text'];
$clean = ['method' => $options['clean']];
switch ($clean['method']) {
'/[\s]*[a-z]+=(")(%s%s%s[\s]*)+\\1/i',
preg_quote($options['before'], '/'),
preg_quote($options['after'], '/')
$str = preg_replace($kleenex, $clean['replacement'], $str);
$options['clean'] = ['method' => 'text'];
$str = static::cleanInsert($str, $options);
'gap' => '[\s]*(?:(?:and|or)[\s]*)?',
preg_quote($options['before'], '/'),
preg_quote($options['after'], '/'),
preg_quote($options['before'], '/'),
preg_quote($options['after'], '/')
$str = preg_replace($kleenex, $clean['replacement'], $str);
* Wraps text to a specific width, can optionally wrap at word breaks.
* - `width` The width to wrap to. Defaults to 72.
* - `wordWrap` Only wrap on words breaks (spaces) Defaults to true.
* - `indent` String to indent with. Defaults to null.
* - `indentAt` 0 based index to start indenting at. Defaults to 0.
* @param string $text The text to format.
* @param array|int $options Array of options to use, or an integer to wrap the text to.
* @return string Formatted text.
public static function wrap($text, $options = [])
if (is_numeric($options)) {
$options = ['width' => $options];
$options += ['width' => 72, 'wordWrap' => true, 'indent' => null, 'indentAt' => 0];
if ($options['wordWrap']) {
$wrapped = self::wordWrap($text, $options['width'], "\n");
$wrapped = trim(chunk_split($text, $options['width'] - 1, "\n"));
if (!empty($options['indent'])) {
$chunks = explode("\n", $wrapped);
for ($i = $options['indentAt'], $len = count($chunks); $i < $len; $i++) {
$chunks[$i] = $options['indent'] . $chunks[$i];
$wrapped = implode("\n", $chunks);
* Wraps a complete block of text to a specific width, can optionally wrap
* - `width` The width to wrap to. Defaults to 72.
* - `wordWrap` Only wrap on words breaks (spaces) Defaults to true.
* - `indent` String to indent with. Defaults to null.
* - `indentAt` 0 based index to start indenting at. Defaults to 0.
* @param string $text The text to format.
* @param array|int $options Array of options to use, or an integer to wrap the text to.
* @return string Formatted text.
public static function wrapBlock($text, $options = [])
if (is_numeric($options)) {
$options = ['width' => $options];
$options += ['width' => 72, 'wordWrap' => true, 'indent' => null, 'indentAt' => 0];
if (!empty($options['indentAt']) && $options['indentAt'] === 0) {
$indentLength = !empty($options['indent']) ? strlen($options['indent']) : 0;
$options['width'] -= $indentLength;
return self::wrap($text, $options);
$wrapped = self::wrap($text, $options);
if (!empty($options['indent'])) {
$indentationLength = mb_strlen($options['indent']);
$chunks = explode("\n", $wrapped);
for ($i = $options['indentAt']; $i < $count; $i++) {
$toRewrap .= mb_substr($chunks[$i], $indentationLength) . ' ';
$options['width'] -= $indentationLength;
$options['indentAt'] = 0;
$rewrapped = self::wrap($toRewrap, $options);
$newChunks = explode("\n", $rewrapped);
$chunks = array_merge($chunks, $newChunks);
$wrapped = implode("\n", $chunks);
* Unicode and newline aware version of wordwrap.
* @param string $text The text to format.
* @param int $width The width to wrap to. Defaults to 72.
* @param string $break The line is broken using the optional break parameter. Defaults to '\n'.
* @param bool $cut If the cut is set to true, the string is always wrapped at the specified width.
* @return string Formatted text.
public static function wordWrap($text, $width = 72, $break = "\n", $cut = false)
$paragraphs = explode($break, $text);
foreach ($paragraphs as &$paragraph) {
$paragraph = static::_wordWrap($paragraph, $width, $break, $cut);
return implode($break, $paragraphs);
* Unicode aware version of wordwrap as helper method.
* @param string $text The text to format.
* @param int $width The width to wrap to. Defaults to 72.
* @param string $break The line is broken using the optional break parameter. Defaults to '\n'.
* @param bool $cut If the cut is set to true, the string is always wrapped at the specified width.
* @return string Formatted text.
protected static function _wordWrap($text, $width = 72, $break = "\n", $cut = false)
while (mb_strlen($text) > 0) {
$part = mb_substr($text, 0, $width);
$text = trim(mb_substr($text, mb_strlen($part)));
return implode($break, $parts);
while (mb_strlen($text) > 0) {
if ($width >= mb_strlen($text)) {
$part = mb_substr($text, 0, $width);
$nextChar = mb_substr($text, $width, 1);
$breakAt = mb_strrpos($part, ' ');
if ($breakAt === false) {
$breakAt = mb_strpos($text, ' ', $width);
if ($breakAt === false) {
$part = mb_substr($text, 0, $breakAt);
$text = trim(mb_substr($text, mb_strlen($part)));
return implode($break, $parts);
* Highlights a given phrase in a text. You can specify any expression in highlighter that
* may include the \1 expression to include the $phrase found.
* - `format` The piece of HTML with that the phrase will be highlighted
* - `html` If true, will ignore any HTML tags, ensuring that only the correct text is highlighted
* - `regex` A custom regex rule that is used to match words, default is '|$tag|iu'
* - `limit` A limit, optional, defaults to -1 (none)
* @param string $text Text to search the phrase in.
* @param string|array $phrase The phrase or phrases that will be searched.
* @param array $options An array of HTML attributes and options.
* @return string The highlighted text
* @link https://book.cakephp.org/3/en/core-libraries/text.html#highlighting-substrings
public static function highlight($text, $phrase, array $options = [])
'format' => '<span class="highlight">\1</span>',