137748cd8SNickeau<?php 237748cd8SNickeau/** 337748cd8SNickeau * Copyright (c) 2020. ComboStrap, Inc. and its affiliates. All Rights Reserved. 437748cd8SNickeau * 537748cd8SNickeau * This source code is licensed under the GPL license found in the 637748cd8SNickeau * COPYING file in the root directory of this source tree. 737748cd8SNickeau * 837748cd8SNickeau * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 937748cd8SNickeau * @author ComboStrap <support@combostrap.com> 1037748cd8SNickeau * 1137748cd8SNickeau */ 1237748cd8SNickeau 1337748cd8SNickeaunamespace ComboStrap; 1437748cd8SNickeau 15*04fd306cSNickeauuse IntlDateFormatter; 16*04fd306cSNickeau 1737748cd8SNickeau/** 1837748cd8SNickeau * Class PipelineUtility 1937748cd8SNickeau * @package ComboStrap 2037748cd8SNickeau * A pipeline to perform filter transformation 2137748cd8SNickeau * 2237748cd8SNickeau * See also 2337748cd8SNickeau * https://getbootstrap.com/docs/5.0/helpers/text-truncation/ 2437748cd8SNickeau */ 2537748cd8SNickeauclass PipelineUtility 2637748cd8SNickeau{ 27*04fd306cSNickeau const QUOTES_CHARACTERS = ['"', '\'']; 28*04fd306cSNickeau const SPACE_CHARACTER = " "; 29*04fd306cSNickeau 3037748cd8SNickeau 3137748cd8SNickeau /** 32*04fd306cSNickeau * @param $expression 33*04fd306cSNickeau * @param array|null $contextData 3437748cd8SNickeau * @return string 35*04fd306cSNickeau * @throws ExceptionBadSyntax - if there is any syntax error 3637748cd8SNickeau */ 37*04fd306cSNickeau static public function execute($expression, array $contextData = null): string 3837748cd8SNickeau { 3937748cd8SNickeau 4037748cd8SNickeau /** 41*04fd306cSNickeau * Get the value (called the message in a pipeline) 4237748cd8SNickeau */ 43*04fd306cSNickeau $processedExpression = $expression; 44*04fd306cSNickeau $firstQuoteChar = strpos($processedExpression, '"'); 45*04fd306cSNickeau $firstPipeChar = strpos($processedExpression, '|'); 46*04fd306cSNickeau if ($firstQuoteChar < $firstPipeChar || $firstPipeChar === false) { 4737748cd8SNickeau 4837748cd8SNickeau /** 49*04fd306cSNickeau * Example: 50*04fd306cSNickeau * a literal: "$title" 51*04fd306cSNickeau * a literal with a pipe: "World | Do" | replace ("world,"you") 5237748cd8SNickeau */ 53*04fd306cSNickeau $message = null; 54*04fd306cSNickeau if ($firstQuoteChar !== false) { 55*04fd306cSNickeau $processedExpression = substr($processedExpression, $firstQuoteChar + 1); 56*04fd306cSNickeau $secondQuoteChar = strpos($processedExpression, '"'); 57*04fd306cSNickeau if ($secondQuoteChar !== false) { 58*04fd306cSNickeau $message = substr($processedExpression, 0, $secondQuoteChar); 59*04fd306cSNickeau } 60*04fd306cSNickeau } 61*04fd306cSNickeau 62*04fd306cSNickeau $pipeCharPosition = strpos($processedExpression, '|'); 63*04fd306cSNickeau if ($pipeCharPosition !== false) { 64*04fd306cSNickeau $commandChain = substr($processedExpression, $pipeCharPosition + 1); 65*04fd306cSNickeau if ($message == null) { 66*04fd306cSNickeau // not quoted expression 67*04fd306cSNickeau // do we support that ? 68*04fd306cSNickeau $message = substr($processedExpression, 0, $pipeCharPosition); 69*04fd306cSNickeau } 70*04fd306cSNickeau } else { 71*04fd306cSNickeau if ($message == null) { 72*04fd306cSNickeau // not quoted expression 73*04fd306cSNickeau // do we support that ? 74*04fd306cSNickeau $message = $processedExpression; 75*04fd306cSNickeau } 76*04fd306cSNickeau $commandChain = ""; 77*04fd306cSNickeau } 78*04fd306cSNickeau 79*04fd306cSNickeau } else { 8037748cd8SNickeau 8137748cd8SNickeau /** 82*04fd306cSNickeau * Example: a variable with an expression 83*04fd306cSNickeau * $title | replace ("world,"you") 8437748cd8SNickeau */ 85*04fd306cSNickeau $message = trim(substr($processedExpression, 0, $firstPipeChar)); 86*04fd306cSNickeau $commandChain = trim(substr($processedExpression, $firstPipeChar + 1)); 87*04fd306cSNickeau 88*04fd306cSNickeau } 89*04fd306cSNickeau 90*04fd306cSNickeau 91*04fd306cSNickeau /** 92*04fd306cSNickeau * Command chain splits 93*04fd306cSNickeau */ 94*04fd306cSNickeau $commands = preg_split("/\|/", $commandChain); 95*04fd306cSNickeau 96*04fd306cSNickeau 97*04fd306cSNickeau /** 98*04fd306cSNickeau * We replace after the split to be sure that there is not a | separator in the variable value 99*04fd306cSNickeau * that would fuck up the process 100*04fd306cSNickeau */ 101*04fd306cSNickeau $message = \syntax_plugin_combo_variable::replaceVariablesWithValuesFromContext($message, $contextData); 102*04fd306cSNickeau 103*04fd306cSNickeau $charactersToTrimFromCommand = implode("", self::QUOTES_CHARACTERS); 10437748cd8SNickeau foreach ($commands as $command) { 10537748cd8SNickeau $command = trim($command, " )"); 10637748cd8SNickeau $leftParenthesis = strpos($command, "("); 10737748cd8SNickeau $commandName = substr($command, 0, $leftParenthesis); 10837748cd8SNickeau $signature = substr($command, $leftParenthesis + 1); 109*04fd306cSNickeau $commandArgs = preg_split("/\s*,\s*/", $signature); 110*04fd306cSNickeau /** 111*04fd306cSNickeau * Delete space characters 112*04fd306cSNickeau */ 11337748cd8SNickeau $commandArgs = array_map( 11437748cd8SNickeau 'trim', 11537748cd8SNickeau $commandArgs, 116*04fd306cSNickeau array_fill(0, sizeof($commandArgs), self::SPACE_CHARACTER) 117*04fd306cSNickeau ); 118*04fd306cSNickeau /** 119*04fd306cSNickeau * Delete quote characters 120*04fd306cSNickeau */ 121*04fd306cSNickeau $commandArgs = array_map( 122*04fd306cSNickeau 'trim', 123*04fd306cSNickeau $commandArgs, 124*04fd306cSNickeau array_fill(0, sizeof($commandArgs), $charactersToTrimFromCommand) 12537748cd8SNickeau ); 12637748cd8SNickeau $commandName = trim($commandName); 12737748cd8SNickeau if (!empty($commandName)) { 12837748cd8SNickeau switch ($commandName) { 12937748cd8SNickeau case "replace": 130*04fd306cSNickeau $message = self::replace($commandArgs, $message); 13137748cd8SNickeau break; 13237748cd8SNickeau case "head": 133*04fd306cSNickeau $message = self::head($commandArgs, $message); 13437748cd8SNickeau break; 13537748cd8SNickeau case "tail": 136*04fd306cSNickeau $message = self::tail($commandArgs, $message); 13737748cd8SNickeau break; 13837748cd8SNickeau case "rconcat": 139*04fd306cSNickeau $message = self::concat($commandArgs, $message, "right"); 14037748cd8SNickeau break; 14137748cd8SNickeau case "lconcat": 142*04fd306cSNickeau $message = self::concat($commandArgs, $message, "left"); 14337748cd8SNickeau break; 14437748cd8SNickeau case "cut": 145*04fd306cSNickeau $message = self::cut($commandArgs, $message); 14637748cd8SNickeau break; 14737748cd8SNickeau case "trim": 148*04fd306cSNickeau $message = trim($message); 14937748cd8SNickeau break; 15037748cd8SNickeau case "capitalize": 151*04fd306cSNickeau $message = ucwords($message); 152*04fd306cSNickeau break; 153*04fd306cSNickeau case "format": 154*04fd306cSNickeau $message = self::format($commandArgs, $message); 15537748cd8SNickeau break; 15637748cd8SNickeau default: 15737748cd8SNickeau LogUtility::msg("command ($commandName) is unknown", LogUtility::LVL_MSG_ERROR, "pipeline"); 15837748cd8SNickeau } 15937748cd8SNickeau } 16037748cd8SNickeau } 161*04fd306cSNickeau return $message; 16237748cd8SNickeau } 16337748cd8SNickeau 16437748cd8SNickeau private static function replace(array $commandArgs, $value) 16537748cd8SNickeau { 16637748cd8SNickeau $search = $commandArgs[0]; 16737748cd8SNickeau $replace = $commandArgs[1]; 16837748cd8SNickeau return str_replace($search, $replace, $value); 16937748cd8SNickeau } 17037748cd8SNickeau 17137748cd8SNickeau /** 17237748cd8SNickeau * @param array $commandArgs 17337748cd8SNickeau * @param $value 17437748cd8SNickeau * @return false|string 17537748cd8SNickeau * See also: https://getbootstrap.com/docs/5.0/helpers/text-truncation/ 17637748cd8SNickeau */ 177*04fd306cSNickeau public static function head(array $commandArgs, $value) 17837748cd8SNickeau { 17937748cd8SNickeau $length = $commandArgs[0]; 1804cadd4f8SNickeau if (strlen($value) < $length) { 1814cadd4f8SNickeau return $value; 1824cadd4f8SNickeau } 183*04fd306cSNickeau $words = explode(" ", $value); 184*04fd306cSNickeau $headValue = ""; 185*04fd306cSNickeau for ($i = 0; $i < sizeof($words); $i++) { 186*04fd306cSNickeau if ($i != 0) { 187*04fd306cSNickeau $headValue .= " "; 188*04fd306cSNickeau } 189*04fd306cSNickeau $headValue .= $words[$i]; 190*04fd306cSNickeau if (strlen($headValue) >= $length) { 191*04fd306cSNickeau break; 192*04fd306cSNickeau } 193*04fd306cSNickeau } 194*04fd306cSNickeau 1954cadd4f8SNickeau $tail = $commandArgs[1]; 1964cadd4f8SNickeau if ($tail !== null) { 1974cadd4f8SNickeau $headValue .= $tail; 1984cadd4f8SNickeau } 199*04fd306cSNickeau 2004cadd4f8SNickeau return $headValue; 20137748cd8SNickeau } 20237748cd8SNickeau 203*04fd306cSNickeau private 204*04fd306cSNickeau static function concat(array $commandArgs, $value, $side): string 20537748cd8SNickeau { 20637748cd8SNickeau $string = $commandArgs[0]; 20737748cd8SNickeau switch ($side) { 20837748cd8SNickeau case "left": 20937748cd8SNickeau return $string . $value; 21037748cd8SNickeau case "right": 21137748cd8SNickeau return $value . $string; 21237748cd8SNickeau default: 21337748cd8SNickeau LogUtility::msg("The side value ($side) is unknown", LogUtility::LVL_MSG_ERROR, "pipeline"); 214*04fd306cSNickeau return $value . $string; 21537748cd8SNickeau } 21637748cd8SNickeau 21737748cd8SNickeau 21837748cd8SNickeau } 21937748cd8SNickeau 220*04fd306cSNickeau private 221*04fd306cSNickeau static function tail(array $commandArgs, $value) 22237748cd8SNickeau { 22337748cd8SNickeau $length = $commandArgs[0]; 22437748cd8SNickeau return substr($value, strlen($value) - $length); 22537748cd8SNickeau } 22637748cd8SNickeau 227*04fd306cSNickeau private 228*04fd306cSNickeau static function cut(array $commandArgs, $value) 22937748cd8SNickeau { 23037748cd8SNickeau $pattern = $commandArgs[0]; 23137748cd8SNickeau $words = preg_split("/$pattern/i", $value); 23237748cd8SNickeau if ($words !== false) { 23337748cd8SNickeau $selector = $commandArgs[1]; 23437748cd8SNickeau $startEndSelector = preg_split("/-/i", $selector); 23537748cd8SNickeau $start = $startEndSelector[0] - 1; 23637748cd8SNickeau $end = null; 23737748cd8SNickeau if (isset($startEndSelector[1])) { 23837748cd8SNickeau $end = $startEndSelector[1]; 23937748cd8SNickeau if (empty($end)) { 24037748cd8SNickeau $end = sizeof($words); 24137748cd8SNickeau } 24237748cd8SNickeau $end = $end - 1; 24337748cd8SNickeau } 24437748cd8SNickeau if ($end == null) { 24537748cd8SNickeau if (isset($words[$start])) { 24637748cd8SNickeau return $words[$start]; 24737748cd8SNickeau } else { 24837748cd8SNickeau return $value; 24937748cd8SNickeau } 25037748cd8SNickeau } else { 25137748cd8SNickeau $result = ""; 25237748cd8SNickeau for ($i = $start; $i <= $end; $i++) { 25337748cd8SNickeau if (isset($words[$i])) { 25437748cd8SNickeau if (!empty($result)) { 25537748cd8SNickeau $result .= $pattern; 25637748cd8SNickeau } 25737748cd8SNickeau $result .= $words[$i]; 25837748cd8SNickeau } 25937748cd8SNickeau } 26037748cd8SNickeau return $result; 26137748cd8SNickeau } 26237748cd8SNickeau 26337748cd8SNickeau } else { 26437748cd8SNickeau return "An error occurred: could not split with the pattern `$pattern`, the value `$value`."; 26537748cd8SNickeau } 26637748cd8SNickeau } 26737748cd8SNickeau 268*04fd306cSNickeau /** 269*04fd306cSNickeau * @throws ExceptionBadSyntax 270*04fd306cSNickeau */ 271*04fd306cSNickeau public 272*04fd306cSNickeau static function format(array $commandArgs, $value): string 273*04fd306cSNickeau { 274*04fd306cSNickeau 275*04fd306cSNickeau /** 276*04fd306cSNickeau * For now only date time are 277*04fd306cSNickeau */ 278*04fd306cSNickeau try { 279*04fd306cSNickeau $dateTime = Iso8601Date::createFromString($value); 280*04fd306cSNickeau } catch (ExceptionBadSyntax $e) { 281*04fd306cSNickeau throw new ExceptionBadSyntax("The format method allows for now only date. The value ($value) is not a date.", PipelineTag::CANONICAL); 282*04fd306cSNickeau } 283*04fd306cSNickeau 284*04fd306cSNickeau $size = sizeof($commandArgs); 285*04fd306cSNickeau $pattern = null; 286*04fd306cSNickeau $locale = null; 287*04fd306cSNickeau switch ($size) { 288*04fd306cSNickeau case 0: 289*04fd306cSNickeau break; 290*04fd306cSNickeau case 1: 291*04fd306cSNickeau $pattern = $commandArgs[0]; 292*04fd306cSNickeau break; 293*04fd306cSNickeau case 2: 294*04fd306cSNickeau default: 295*04fd306cSNickeau $pattern = $commandArgs[0]; 296*04fd306cSNickeau $locale = $commandArgs[1]; 297*04fd306cSNickeau break; 298*04fd306cSNickeau } 299*04fd306cSNickeau $localeSeparator = '_'; 300*04fd306cSNickeau if ($locale === null) { 301*04fd306cSNickeau $path = ExecutionContext::getActualOrCreateFromEnv()->getContextPath(); 302*04fd306cSNickeau $page = MarkupPath::createPageFromPathObject($path); 303*04fd306cSNickeau $locale = Locale::createForPage($page)->getValueOrDefault(); 304*04fd306cSNickeau } 305*04fd306cSNickeau 306*04fd306cSNickeau if ($locale === null) { 307*04fd306cSNickeau // should never happen but yeah 308*04fd306cSNickeau $locale = 'en_US'; 309*04fd306cSNickeau LogUtility::error("Internal Error: No default locale could be determined. The locale was set to $locale", DateTag::CANONICAL); 310*04fd306cSNickeau } 311*04fd306cSNickeau 312*04fd306cSNickeau /** 313*04fd306cSNickeau * If the user has set a lang 314*04fd306cSNickeau * Transform it as locale 315*04fd306cSNickeau */ 316*04fd306cSNickeau if (strlen(trim($locale)) === 2) { 317*04fd306cSNickeau $derivedLocale = strtolower($locale) . $localeSeparator . strtoupper($locale); 318*04fd306cSNickeau } else { 319*04fd306cSNickeau $derivedLocale = $locale; 320*04fd306cSNickeau } 321*04fd306cSNickeau 322*04fd306cSNickeau return $dateTime->formatLocale($pattern, $derivedLocale); 323*04fd306cSNickeau 324*04fd306cSNickeau } 325*04fd306cSNickeau 32637748cd8SNickeau} 327