/** * Shorten a string to a given number of characters * * The function preserves words, so the result might be a bit shorter or * longer than the number of characters given. It strips all tags. * * @param string $strString The string to shorten * @param integer $intNumberOfChars The target number of characters * @param string $strEllipsis An optional ellipsis to append to the shortened string * * @return string The shortened string */ public static function substr($strString, $intNumberOfChars, $strEllipsis = ' …') { $strString = preg_replace('/[\\t\\n\\r]+/', ' ', $strString); $strString = strip_tags($strString); if (Utf8::strlen($strString) <= $intNumberOfChars) { return $strString; } $intCharCount = 0; $arrWords = array(); $arrChunks = preg_split('/\\s+/', $strString); $blnAddEllipsis = false; foreach ($arrChunks as $strChunk) { $intCharCount += Utf8::strlen(static::decodeEntities($strChunk)); if ($intCharCount++ <= $intNumberOfChars) { $arrWords[] = $strChunk; continue; } // If the first word is longer than $intNumberOfChars already, shorten it // with Utf8::substr() so the method does not return an empty string. if (empty($arrWords)) { $arrWords[] = Utf8::substr($strChunk, 0, $intNumberOfChars); } if ($strEllipsis !== false) { $blnAddEllipsis = true; } break; } // Deprecated since Contao 4.0, to be removed in Contao 5.0 if ($strEllipsis === true) { @trigger_error('Passing "true" as third argument to StringUtil::substr() has been deprecated and will no longer work in Contao 5.0. Pass the ellipsis string instead.', E_USER_DEPRECATED); $strEllipsis = ' …'; } return implode(' ', $arrWords) . ($blnAddEllipsis ? $strEllipsis : ''); }
/** * Remove unsafe characters for URL slug * * @param string $handle * @param int $maxlength = Max number of characters of the return value * @return string $handle */ public function slugSafeString($handle, $maxlength = 128) { $handle = preg_replace('/[^\\p{L}\\p{Nd}\\-_]+/u', ' ', $handle); // remove unneeded chars $handle = preg_replace('/[-\\s]+/', '-', $handle); // convert spaces to hyphens return trim(Utf8::substr($handle, 0, $maxlength), '-'); // trim to first $max_length chars }
/** * Return the formatted group header as string * * @param string $field * @param mixed $value * @param integer $mode * * @return string */ protected function formatCurrentValue($field, $value, $mode) { $remoteNew = $value; // see #3861 if ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['inputType'] == 'checkbox' && !$GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['eval']['multiple']) { $remoteNew = $value != '' ? ucfirst($GLOBALS['TL_LANG']['MSC']['yes']) : ucfirst($GLOBALS['TL_LANG']['MSC']['no']); } elseif (isset($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['foreignKey'])) { $key = explode('.', $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['foreignKey'], 2); $objParent = $this->Database->prepare("SELECT " . $key[1] . " AS value FROM " . $key[0] . " WHERE id=?")->limit(1)->execute($value); if ($objParent->numRows) { $remoteNew = $objParent->value; } } elseif (in_array($mode, array(1, 2))) { $remoteNew = $value != '' ? ucfirst(Utf8::substr($value, 0, 1)) : '-'; } elseif (in_array($mode, array(3, 4))) { if (!isset($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['length'])) { $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['length'] = 2; } $remoteNew = $value != '' ? ucfirst(Utf8::substr($value, 0, $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['length'])) : '-'; } elseif (in_array($mode, array(5, 6))) { $remoteNew = $value != '' ? \Date::parse(\Config::get('dateFormat'), $value) : '-'; } elseif (in_array($mode, array(7, 8))) { $remoteNew = $value != '' ? date('Y-m', $value) : '-'; $intMonth = $value != '' ? date('m', $value) - 1 : '-'; if (isset($GLOBALS['TL_LANG']['MONTHS'][$intMonth])) { $remoteNew = $value != '' ? $GLOBALS['TL_LANG']['MONTHS'][$intMonth] . ' ' . date('Y', $value) : '-'; } } elseif (in_array($mode, array(9, 10))) { $remoteNew = $value != '' ? date('Y', $value) : '-'; } else { if ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['inputType'] == 'checkbox' && !$GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['eval']['multiple']) { $remoteNew = $value != '' ? $field : ''; } elseif (is_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['reference'])) { $remoteNew = $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['reference'][$value]; } elseif ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['eval']['isAssociative'] || array_is_assoc($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['options'])) { $remoteNew = $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['options'][$value]; } else { $remoteNew = $value; } if (is_array($remoteNew)) { $remoteNew = $remoteNew[0]; } if (empty($remoteNew)) { $remoteNew = '-'; } } return $remoteNew; }
/** * @covers Patchwork\Utf8::substr */ function testSubstr() { $b = "deja"; $c = "déjà"; $d = n::normalize($c, n::NFD); $this->assertTrue($c > $d); $this->assertSame('국어', u::substr('한국어', 1, 20)); $this->assertSame("de", substr($b, 0, 2)); $this->assertSame("ja", substr($b, -2, 3)); $this->assertSame("ej", substr($b, -3, -1)); $this->assertSame("", substr($b, 1, -3)); $this->assertSame("", substr($c, 5, 0)); // u::substr() returns false here $this->assertSame("", substr($c, -5, 0)); // u::substr() returns false here $this->assertSame(false, substr($b, 1, -4)); $this->assertSame("jà", u::substr($c, 2)); $this->assertSame("jà", u::substr($c, -2)); $this->assertSame("dé", u::substr($c, 0, 2)); $this->assertSame("jà", u::substr($c, -2, 3)); $this->assertSame("éj", u::substr($c, -3, -1)); $this->assertSame("", u::substr($c, 1, -3)); $this->assertSame(false, u::substr($c, 5, 0)); // Modelled after grapheme_substr(), not substr() (see above) $this->assertSame(false, u::substr($c, -5, 0)); // Modelled after grapheme_substr(), not substr() (see above) $this->assertSame(false, u::substr($c, 1, -4)); $this->assertSame(n::normalize("dé", n::NFD), u::substr($d, 0, 2)); $this->assertSame(n::normalize("jà", n::NFD), u::substr($d, -2, 3)); $this->assertSame(n::normalize("éj", n::NFD), u::substr($d, -3, -1)); $this->assertSame("", u::substr($d, 1, -3)); $this->assertSame(false, u::substr($d, 1, -4)); }
/** // Gets part of a string. * @param string $string The input string. * @param int $start The first position from which the extracted part begins. * @param int $length The length in character of the extracted part. * @param string $encoding (optional) The used internally by this function character encoding. If it is omitted, the platform character set will be used by default. * @return string Returns the part of the string specified by the start and length parameters. * Note: First character's position is 0. Second character position is 1, and so on. * This function is aimed at replacing the functions substr() and mb_substr() for human-language strings. * @link http://php.net/manual/en/function.substr * @link http://php.net/manual/en/function.mb-substr */ function api_substr($string, $start, $length = null, $encoding = null) { if (is_null($length)) { $length = api_strlen($string, $encoding); } return Utf8::substr($string, $start, $length); }
public function test_long_length() { $str = 'Iñtërnâtiônàlizætiøn'; $this->assertEquals('Iñtërnâtiônàlizætiøn', u::substr($str, 0, 15536)); }
public function part($start, $length = null) { $this->string = u::substr($this->string, $start, $length); return $this; }
/** * Return substring of a string * * @param string $str * @param integer $start * @param integer $length * * @return string * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use Patchwork\Utf8::substr() instead. */ function utf8_substr($str, $start, $length = null) { @trigger_error('Using utf8_substr() has been deprecated and will no longer work in Contao 5.0. Use Patchwork\\Utf8::substr() instead.', E_USER_DEPRECATED); return Utf8::substr($str, $start, $length); }
/** * Search the index and return the result object * * @param string $strKeywords The keyword string * @param boolean $blnOrSearch If true, the result can contain any keyword * @param array $arrPid An optional array of page IDs to limit the result to * @param integer $intRows An optional maximum number of result rows * @param integer $intOffset An optional result offset * @param boolean $blnFuzzy If true, the search will be fuzzy * * @return Database\Result The database result object * * @throws \Exception If the cleaned keyword string is empty */ public static function searchFor($strKeywords, $blnOrSearch = false, $arrPid = array(), $intRows = 0, $intOffset = 0, $blnFuzzy = false) { // Clean the keywords $strKeywords = Utf8::strtolower($strKeywords); $strKeywords = \StringUtil::decodeEntities($strKeywords); $strKeywords = preg_replace(array('/\\. /', '/\\.$/', '/: /', '/:$/', '/, /', '/,$/', '/[^\\w\' *+".:,-]/u'), ' ', $strKeywords); // Check keyword string if (!strlen($strKeywords)) { throw new \Exception('Empty keyword string'); } // Split keywords $arrChunks = array(); preg_match_all('/"[^"]+"|[\\+\\-]?[^ ]+\\*?/', $strKeywords, $arrChunks); $arrPhrases = array(); $arrKeywords = array(); $arrWildcards = array(); $arrIncluded = array(); $arrExcluded = array(); foreach ($arrChunks[0] as $strKeyword) { if (substr($strKeyword, -1) == '*' && strlen($strKeyword) > 1) { $arrWildcards[] = str_replace('*', '%', $strKeyword); continue; } switch (substr($strKeyword, 0, 1)) { // Phrases case '"': if (($strKeyword = trim(substr($strKeyword, 1, -1))) != false) { $arrPhrases[] = '[[:<:]]' . str_replace(array(' ', '*'), array('[^[:alnum:]]+', ''), $strKeyword) . '[[:>:]]'; } break; // Included keywords // Included keywords case '+': if (($strKeyword = trim(substr($strKeyword, 1))) != false) { $arrIncluded[] = $strKeyword; } break; // Excluded keywords // Excluded keywords case '-': if (($strKeyword = trim(substr($strKeyword, 1))) != false) { $arrExcluded[] = $strKeyword; } break; // Wildcards // Wildcards case '*': if (strlen($strKeyword) > 1) { $arrWildcards[] = str_replace('*', '%', $strKeyword); } break; // Normal keywords // Normal keywords default: $arrKeywords[] = $strKeyword; break; } } // Fuzzy search if ($blnFuzzy) { foreach ($arrKeywords as $strKeyword) { $arrWildcards[] = '%' . $strKeyword . '%'; } $arrKeywords = array(); } // Count keywords $intPhrases = count($arrPhrases); $intWildcards = count($arrWildcards); $intIncluded = count($arrIncluded); $intExcluded = count($arrExcluded); $intKeywords = 0; $arrValues = array(); // Remember found words so we can highlight them later $strQuery = "SELECT tl_search_index.pid AS sid, GROUP_CONCAT(word) AS matches"; // Get the number of wildcard matches if (!$blnOrSearch && $intWildcards) { $strQuery .= ", (SELECT COUNT(*) FROM tl_search_index WHERE (" . implode(' OR ', array_fill(0, $intWildcards, 'word LIKE ?')) . ") AND pid=sid) AS wildcards"; $arrValues = array_merge($arrValues, $arrWildcards); } // Count the number of matches $strQuery .= ", COUNT(*) AS count"; // Get the relevance $strQuery .= ", SUM(relevance) AS relevance"; // Get meta information from tl_search $strQuery .= ", tl_search.*"; // see #4506 // Prepare keywords array $arrAllKeywords = array(); // Get keywords if (!empty($arrKeywords)) { $arrAllKeywords[] = implode(' OR ', array_fill(0, count($arrKeywords), 'word=?')); $arrValues = array_merge($arrValues, $arrKeywords); $intKeywords += count($arrKeywords); } // Get included keywords if ($intIncluded) { $arrAllKeywords[] = implode(' OR ', array_fill(0, $intIncluded, 'word=?')); $arrValues = array_merge($arrValues, $arrIncluded); $intKeywords += $intIncluded; } // Get keywords from phrases if ($intPhrases) { foreach ($arrPhrases as $strPhrase) { $arrWords = explode('[^[:alnum:]]+', Utf8::substr($strPhrase, 7, -7)); $arrAllKeywords[] = implode(' OR ', array_fill(0, count($arrWords), 'word=?')); $arrValues = array_merge($arrValues, $arrWords); $intKeywords += count($arrWords); } } // Get wildcards if ($intWildcards) { $arrAllKeywords[] = implode(' OR ', array_fill(0, $intWildcards, 'word LIKE ?')); $arrValues = array_merge($arrValues, $arrWildcards); } $strQuery .= " FROM tl_search_index LEFT JOIN tl_search ON(tl_search_index.pid=tl_search.id) WHERE (" . implode(' OR ', $arrAllKeywords) . ")"; // Get phrases if ($intPhrases) { $strQuery .= " AND (" . implode($blnOrSearch ? ' OR ' : ' AND ', array_fill(0, $intPhrases, 'tl_search_index.pid IN(SELECT id FROM tl_search WHERE text REGEXP ?)')) . ")"; $arrValues = array_merge($arrValues, $arrPhrases); } // Include keywords if ($intIncluded) { $strQuery .= " AND tl_search_index.pid IN(SELECT pid FROM tl_search_index WHERE " . implode(' OR ', array_fill(0, $intIncluded, 'word=?')) . ")"; $arrValues = array_merge($arrValues, $arrIncluded); } // Exclude keywords if ($intExcluded) { $strQuery .= " AND tl_search_index.pid NOT IN(SELECT pid FROM tl_search_index WHERE " . implode(' OR ', array_fill(0, $intExcluded, 'word=?')) . ")"; $arrValues = array_merge($arrValues, $arrExcluded); } // Limit results to a particular set of pages if (!empty($arrPid) && is_array($arrPid)) { $strQuery .= " AND tl_search_index.pid IN(SELECT id FROM tl_search WHERE pid IN(" . implode(',', array_map('intval', $arrPid)) . "))"; } $strQuery .= " GROUP BY tl_search_index.pid"; // Make sure to find all words if (!$blnOrSearch) { // Number of keywords without wildcards $strQuery .= " HAVING count >= " . $intKeywords; // Dynamically add the number of wildcard matches if ($intWildcards) { $strQuery .= " + IF(wildcards>" . $intWildcards . ", wildcards, " . $intWildcards . ")"; } } // Sort by relevance $strQuery .= " ORDER BY relevance DESC"; // Return result $objResultStmt = \Database::getInstance()->prepare($strQuery); if ($intRows > 0) { $objResultStmt->limit($intRows, $intOffset); } return $objResultStmt->execute($arrValues); }