* */ namespace ComboStrap; use IntlDateFormatter; /** * Class PipelineUtility * @package ComboStrap * A pipeline to perform filter transformation * * See also * https://getbootstrap.com/docs/5.0/helpers/text-truncation/ */ class PipelineUtility { const QUOTES_CHARACTERS = ['"', '\'']; const SPACE_CHARACTER = " "; /** * @param $expression * @param array|null $contextData * @return string * @throws ExceptionBadSyntax - if there is any syntax error */ static public function execute($expression, array $contextData = null): string { /** * Get the value (called the message in a pipeline) */ $processedExpression = $expression; $firstQuoteChar = strpos($processedExpression, '"'); $firstPipeChar = strpos($processedExpression, '|'); if ($firstQuoteChar < $firstPipeChar || $firstPipeChar === false) { /** * Example: * a literal: "$title" * a literal with a pipe: "World | Do" | replace ("world,"you") */ $message = null; if ($firstQuoteChar !== false) { $processedExpression = substr($processedExpression, $firstQuoteChar + 1); $secondQuoteChar = strpos($processedExpression, '"'); if ($secondQuoteChar !== false) { $message = substr($processedExpression, 0, $secondQuoteChar); } } $pipeCharPosition = strpos($processedExpression, '|'); if ($pipeCharPosition !== false) { $commandChain = substr($processedExpression, $pipeCharPosition + 1); if ($message == null) { // not quoted expression // do we support that ? $message = substr($processedExpression, 0, $pipeCharPosition); } } else { if ($message == null) { // not quoted expression // do we support that ? $message = $processedExpression; } $commandChain = ""; } } else { /** * Example: a variable with an expression * $title | replace ("world,"you") */ $message = trim(substr($processedExpression, 0, $firstPipeChar)); $commandChain = trim(substr($processedExpression, $firstPipeChar + 1)); } /** * Command chain splits */ $commands = preg_split("/\|/", $commandChain); /** * We replace after the split to be sure that there is not a | separator in the variable value * that would fuck up the process */ $message = \syntax_plugin_combo_variable::replaceVariablesWithValuesFromContext($message, $contextData); $charactersToTrimFromCommand = implode("", self::QUOTES_CHARACTERS); foreach ($commands as $command) { $command = trim($command, " )"); $leftParenthesis = strpos($command, "("); $commandName = substr($command, 0, $leftParenthesis); $signature = substr($command, $leftParenthesis + 1); $commandArgs = preg_split("/\s*,\s*/", $signature); /** * Delete space characters */ $commandArgs = array_map( 'trim', $commandArgs, array_fill(0, sizeof($commandArgs), self::SPACE_CHARACTER) ); /** * Delete quote characters */ $commandArgs = array_map( 'trim', $commandArgs, array_fill(0, sizeof($commandArgs), $charactersToTrimFromCommand) ); $commandName = trim($commandName); if (!empty($commandName)) { switch ($commandName) { case "replace": $message = self::replace($commandArgs, $message); break; case "head": $message = self::head($commandArgs, $message); break; case "tail": $message = self::tail($commandArgs, $message); break; case "rconcat": $message = self::concat($commandArgs, $message, "right"); break; case "lconcat": $message = self::concat($commandArgs, $message, "left"); break; case "cut": $message = self::cut($commandArgs, $message); break; case "trim": $message = trim($message); break; case "capitalize": $message = ucwords($message); break; case "format": $message = self::format($commandArgs, $message); break; default: LogUtility::msg("command ($commandName) is unknown", LogUtility::LVL_MSG_ERROR, "pipeline"); } } } return $message; } private static function replace(array $commandArgs, $value) { $search = $commandArgs[0]; $replace = $commandArgs[1]; return str_replace($search, $replace, $value); } /** * @param array $commandArgs * @param $value * @return false|string * See also: https://getbootstrap.com/docs/5.0/helpers/text-truncation/ */ public static function head(array $commandArgs, $value) { $length = $commandArgs[0]; if (strlen($value) < $length) { return $value; } $words = explode(" ", $value); $headValue = ""; for ($i = 0; $i < sizeof($words); $i++) { if ($i != 0) { $headValue .= " "; } $headValue .= $words[$i]; if (strlen($headValue) >= $length) { break; } } $tail = $commandArgs[1] ?? null; if ($tail !== null) { $headValue .= $tail; } return $headValue; } private static function concat(array $commandArgs, $value, $side): string { $string = $commandArgs[0]; switch ($side) { case "left": return $string . $value; case "right": return $value . $string; default: LogUtility::msg("The side value ($side) is unknown", LogUtility::LVL_MSG_ERROR, "pipeline"); return $value . $string; } } private static function tail(array $commandArgs, $value) { $length = $commandArgs[0]; return substr($value, strlen($value) - $length); } private static function cut(array $commandArgs, $value) { $pattern = $commandArgs[0]; $words = preg_split("/$pattern/i", $value); if ($words !== false) { $selector = $commandArgs[1]; $startEndSelector = preg_split("/-/i", $selector); $start = $startEndSelector[0] - 1; $end = null; if (isset($startEndSelector[1])) { $end = $startEndSelector[1]; if (empty($end)) { $end = sizeof($words); } $end = $end - 1; } if ($end == null) { if (isset($words[$start])) { return $words[$start]; } else { return $value; } } else { $result = ""; for ($i = $start; $i <= $end; $i++) { if (isset($words[$i])) { if (!empty($result)) { $result .= $pattern; } $result .= $words[$i]; } } return $result; } } else { return "An error occurred: could not split with the pattern `$pattern`, the value `$value`."; } } /** * @throws ExceptionBadSyntax */ public static function format(array $commandArgs, $value): string { /** * For now only date time are */ try { $dateTime = Iso8601Date::createFromString($value); } catch (ExceptionBadSyntax $e) { throw new ExceptionBadSyntax("The format method allows for now only date. The value ($value) is not a date.", PipelineTag::CANONICAL); } $size = sizeof($commandArgs); $pattern = null; $locale = null; switch ($size) { case 0: break; case 1: $pattern = $commandArgs[0]; break; case 2: default: $pattern = $commandArgs[0]; $locale = $commandArgs[1]; break; } $localeSeparator = '_'; if ($locale === null) { $path = ExecutionContext::getActualOrCreateFromEnv()->getContextPath(); $page = MarkupPath::createPageFromPathObject($path); $locale = Locale::createForPage($page)->getValueOrDefault(); } if ($locale === null) { // should never happen but yeah $locale = 'en_US'; LogUtility::error("Internal Error: No default locale could be determined. The locale was set to $locale", DateTag::CANONICAL); } /** * If the user has set a lang * Transform it as locale */ if (strlen(trim($locale)) === 2) { $derivedLocale = strtolower($locale) . $localeSeparator . strtoupper($locale); } else { $derivedLocale = $locale; } return $dateTime->formatLocale($pattern, $derivedLocale); } }