diff --git a/Documentation/Concept.rst b/Documentation/Concept.rst index 7d3bf589c9200975486ae8b2c7ded999d4671af1..d6d7dbde60ef0247c61951b4a04e9c5a8b63c871 100644 --- a/Documentation/Concept.rst +++ b/Documentation/Concept.rst @@ -89,98 +89,100 @@ QFQ Keywords (Bodytext) **All of these parameters are optional.** -+-------------------------+---------------------------------------------------------------------------------+ -| Name | Explanation | -+=========================+=================================================================================+ -| form | | Formname. | -| | | Static: **form = person** | -| | | By SIP: **form = {{form:SE}}** | -| | | By SQL: **form = {{SELECT c.form FROM Config AS c WHERE c.id={{a:C}} }}** | -+-------------------------+---------------------------------------------------------------------------------+ -| r | | <record id>. The form will load the record with the specified id. | -| | | Static: **r = 123** | -| | | By SQL: **r = {{SELECT ...}}** | -| | | If not specified, the SIP parameter 'r' is used. | -+-------------------------+---------------------------------------------------------------------------------+ -| dbIndex | E.g. `dbIndex = {{indexQfq:Y}}` Select a DB index. Only necessary if a | -| | different than the standard DB should be used. | -+-------------------------+---------------------------------------------------------------------------------+ -| debugShowBodyText | If='1' and :ref:`configuration`:*showDebugInfo: yes*, shows a | -| | tooltip with bodytext | -+-------------------------+---------------------------------------------------------------------------------+ -| sqlLog | Overwrites :ref:`configuration`: :ref:`SQL_LOG` . Only affects `Report`, | -| | not `Form`. | -+-------------------------+---------------------------------------------------------------------------------+ -| sqlLogMode | Overwrites :ref:`configuration`: :ref:`SQL_LOG_MODE<SQL_LOG_MODE>` . | -| | Only affects `Report`, not `Form`. | -+-------------------------+---------------------------------------------------------------------------------+ -| render | See :ref:`report-render`. Overwrites :ref:`configuration`: render. | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.fbeg | Start token for every field (=column) | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.fend | End token for every field (=column) | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.fsep | Separator token between fields (=columns) | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.fskipwrap | Skip wrapping (via fbeg, fsep, fend) of named columns. Comma separated list of | -| | column id's (starting at 1). See also the special column name '_noWrap' to | -| | suppress wrapping. | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.shead | Static start token for whole <level>, independent if records are selected | -| | Shown before `head`. | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.stail | Static end token for whole <level>, independent if records are selected. | -| | Shown after `tail`. | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.head | Dynamic start token for whole <level>. Only if at least one record is select. | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.tail | Dynamic end token for whole <level>. Only if at least one record is select. | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.rbeg | Start token for row. | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.rbgd | Alternating (per row) token. | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.rend | End token for row. Will be rendered **before** subsequent levels are processed | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.renr | End token for row. Will be rendered **after** subsequent levels are processed | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.rsep | Seperator token between rows | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.sql | SQL Query | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.twig | Twig Template | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.althead | If <level>.sql has no rows selected (empty), these token will be rendered. | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.altsql | If <level>.sql has no rows selected (empty) or affected (delete, update, insert)| -| | the <altsql> will be fired. Note: Sub queries of <level> are not fired, even if | -| | <altsql> selects some rows. | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.content | | *show* (default): content of current and sub level are directly shown. | -| | | *hide*: content of current and sub levels are **stored** and not shown. | -| | | *hideLevel*: content of current and sub levels are **stored** and only sub | -| | | levels are shown. | -| | | *store*: content of current and sub levels are **stored** and shown. | -| | | To retrieve the content: `{{<level>.line.content}}`. | -| | | See :ref:`syntax-of-report` | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.line.count | Current row index. Will be replaced before the query is fired in case of | -| | ``<level>`` is an outer/previous level or it will be replaced after a query is | -| | fired in case ``<level>`` is the current level. | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.line.total | Total rows (MySQL ``num_rows`` for *SELECT* and *SHOW*, MySQL ``affected_rows`` | -| | for *UPDATE* and *INSERT*. | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.line.insertId | Last insert id for *INSERT*. | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.line.content | Show content of `<level>` (content have to be stored via <level>.content=....) | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.line.altCount | Like 'line.count' but for 'alt' query. | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.line.altTotal | Like 'line.total' but for 'alt' query. | -+-------------------------+---------------------------------------------------------------------------------+ -| <level>.line.altInsertId| Like 'line.insertId' but for 'alt' query. | -+-------------------------+---------------------------------------------------------------------------------+ ++--------------------------------+---------------------------------------------------------------------------------+ +| Name | Explanation | ++================================+=================================================================================+ +| form | | Formname. | +| | | Static: **form = person** | +| | | By SIP: **form = {{form:SE}}** | +| | | By SQL: **form = {{SELECT c.form FROM Config AS c WHERE c.id={{a:C}} }}** | ++--------------------------------+---------------------------------------------------------------------------------+ +| r | | <record id>. The form will load the record with the specified id. | +| | | Static: **r = 123** | +| | | By SQL: **r = {{SELECT ...}}** | +| | | If not specified, the SIP parameter 'r' is used. | ++--------------------------------+---------------------------------------------------------------------------------+ +| dbIndex | E.g. `dbIndex = {{indexQfq:Y}}` Select a DB index. Only necessary if a | +| | different than the standard DB should be used. | ++--------------------------------+---------------------------------------------------------------------------------+ +| debugShowBodyText | If='1' and :ref:`configuration`:*showDebugInfo: yes*, shows a | +| | tooltip with bodytext | ++--------------------------------+---------------------------------------------------------------------------------+ +| sqlLog | Overwrites :ref:`configuration`: :ref:`SQL_LOG` . Only affects `Report`, | +| | not `Form`. | ++--------------------------------+---------------------------------------------------------------------------------+ +| sqlLogMode | Overwrites :ref:`configuration`: :ref:`SQL_LOG_MODE<SQL_LOG_MODE>` . | +| | Only affects `Report`, not `Form`. | ++--------------------------------+---------------------------------------------------------------------------------+ +| render | See :ref:`report-render`. Overwrites :ref:`configuration`: render. | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.fbeg | Start token for every field (=column) | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.fend | End token for every field (=column) | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.fsep | Separator token between fields (=columns) | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.fskipwrap | Skip wrapping (via fbeg, fsep, fend) of named columns. Comma separated list of | +| | column id's (starting at 1). See also the :ref:`special-column-names` '_noWrap' | +| | to suppress wrapping. | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.shead | Static start token for whole <level>, independent if records are selected | +| | Shown before `head`. | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.stail | Static end token for whole <level>, independent if records are selected. | +| | Shown after `tail`. | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.head | Dynamic start token for whole <level>. Only if at least one record is select. | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.tail | Dynamic end token for whole <level>. Only if at least one record is select. | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.rbeg | Start token for row. | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.rbgd | Alternating (per row) token. | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.rend | End token for row. Will be rendered **before** subsequent levels are processed | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.renr | End token for row. Will be rendered **after** subsequent levels are processed | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.rsep | Seperator token between rows | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.sql | SQL Query | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.twig | Twig Template | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.althead | If <level>.sql has no rows selected (empty), these token will be rendered. | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.altsql | If <level>.sql has no rows selected (empty) or affected (delete, update, insert)| +| | the <altsql> will be fired. Note: Sub queries of <level> are not fired, even if | +| | <altsql> selects some rows. | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level>.content | | *show* (default): content of current and sub level are directly shown. | +| | | *hide*: content of current and sub levels are **stored** and not shown. | +| | | *hideLevel*: content of current and sub levels are **stored** and only sub | +| | | levels are shown. | +| | | *store*: content of current and sub levels are **stored** and shown. | +| | | To retrieve the content: `{{<level>.line.content}}`. | +| | | See :ref:`syntax-of-report` | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level|alias>.line.count | Current row index. Will be replaced before the query is fired in case of | +| | ``<level>``/``<alias>`` is an outer/previous level or it will be replaced after | +| | a query is fired in case ``<level>``/``<alias>`` is the current level. | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level|alias>.line.total | Total rows (MySQL ``num_rows`` for *SELECT* and *SHOW*, MySQL ``affected_rows`` | +| | for *UPDATE* and *INSERT*. | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level|alias>.line.insertId | Last insert id for *INSERT*. | +| <alias>.line.insertId | | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level|alias>.line.content | Show content of `<level>`/`<alias>` (content have to be stored via | +| | <level>.content=... or <alias>.content=...). | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level|alias>.line.altCount | Like 'line.count' but for 'alt' query. | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level|alias>.line.altTotal | Like 'line.total' but for 'alt' query. | ++--------------------------------+---------------------------------------------------------------------------------+ +| <level|alias>.line.altInsertId | Like 'line.insertId' but for 'alt' query. | ++--------------------------------+---------------------------------------------------------------------------------+ .. _`report-render`: diff --git a/Documentation/Report.rst b/Documentation/Report.rst index e4cf107554758fb7c7d76377d573c7b6858d5d73..9ac07ad5b681bc94767079f34ef71e6206af5903 100644 --- a/Documentation/Report.rst +++ b/Documentation/Report.rst @@ -398,8 +398,8 @@ To get the same result, the following is also possible:: '|p:/export', '|t:Download') AS _pdf -Nesting of levels -^^^^^^^^^^^^^^^^^ +Nesting of levels: `numeric` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Levels can be nested. E.g.:: @@ -417,6 +417,58 @@ This is equal to:: 10.5.sql = SELECT ... 10.5.head = ... +Nesting of levels: `alias` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Levels can be nested without levels. E.g.:: + + { + sql = SELECT ... + { + sql = SELECT ... + head = ... + } + } + +This is equal to:: + + 1.sql = SELECT ... + 1.2.sql = SELECT ... + 1.2.head = ... + +Levels are automatically numbered from top to bottom. + +An alias can be used instead of levels. E.g.:: + + myAlias { + sql = SELECT ... + nextAlias { + sql = SELECT ... + head = ... + } + } + +This is also equal to:: + + 1.sql = SELECT ... + 1.2.sql = SELECT ... + 1.2.head = ... + +.. important:: + +Allowed characters for an alias: [a-zA-Z0-9_-]. + +.. important:: + +The first level determines whether report notation `numeric` or `alias` is used. Using an alias or no level triggers +report notation `alias`. It requires the use of delimiters throughout the report. A combination with the notation +'10.sql = ...' is not possible. + +.. important:: + +Report notation `alias` does not require that each level be assigned an alias. If an alias is used, it must be on the +same line as the opening delimiter. + Leading / trailing spaces ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -532,7 +584,20 @@ Example 'level':: 10.5.20.sql = SELECT '{{10.pId}}' 10.10.sql = SELECT '{{10.pId}}' +Example 'alias':: + myAlias { + sql = SELECT p.id AS _pId, p.name FROM Person AS p + myAlias2 { + sql = SELECT adr.city, 'dummy' AS _pId FROM Address AS adr WHERE adr.pId={{10.pId}} + myAlias3 { + sql = SELECT '{{myAlias.pId}}' + } + } + myAlias4 { + sql = SELECT '{{myAlias.pId}}' + } + } Notes to the level: +-------------+------------------------------------------------------------------------------------------------------------------------+ @@ -546,6 +611,8 @@ Notes to the level: +-------------+------------------------------------------------------------------------------------------------------------------------+ | Child |The level *30* has one child and child child: *30.5* and *30.5.1* | +-------------+------------------------------------------------------------------------------------------------------------------------+ +| Alias |A variable that can be assigned to a level and used to retrieve its values. | ++-------------+------------------------------------------------------------------------------------------------------------------------+ | Example | *10*, *20*, *30*, *50** are root level and will be completely processed one after each other. | | | *30.5* will be executed as many times as *30* has row numbers. | | | *30.5.1* will be executed as many times as *30.5* has row numbers. | diff --git a/Documentation/Variable.rst b/Documentation/Variable.rst index 12b6552e24d8e900a60e6e21bf92f154130c028f..423adc417caa87f3d9ed69cced48d99ea7635824 100644 --- a/Documentation/Variable.rst +++ b/Documentation/Variable.rst @@ -410,11 +410,11 @@ Example:: Row column variables -------------------- -Syntax: *{{<level>.<column>}}* +Syntax: *{{<level>.<column>}}* or *{{<alias>.<column>}}* Only used in report to access outer columns. See :ref:`access-column-values` and :ref:`syntax-of-report`. -There might be name conflicts between VarName / SQL keywords and <line identifier>. QFQ checks first for *<level>*, +There might be name conflicts between VarName / SQL keywords and <line identifier>. QFQ checks first for *<level>* and *<alias>*, than for *SQL keywords* and than for *VarNames* in stores. All types might be nested with each other. There is no limit of nesting variables. diff --git a/extension/Classes/Core/BodytextParser.php b/extension/Classes/Core/BodytextParser.php index 2b5394e383fdd34607fbd27b20bd20f5e90b30ef..6febadae7de01fa881b22fc3606561f23b93fcdf 100644 --- a/extension/Classes/Core/BodytextParser.php +++ b/extension/Classes/Core/BodytextParser.php @@ -49,6 +49,8 @@ class BodytextParser { json_encode([ERROR_MESSAGE_TO_USER => 'Report: Missing close delimiter', ERROR_MESSAGE_TO_DEVELOPER => $bodyText]), ERROR_MISSING_CLOSE_DELIMITER); } + unset($this->reportLinesTemp); + return $bodyText; } @@ -64,6 +66,7 @@ class BodytextParser { private function trimAndRemoveCommentAndEmptyLine($bodytext, &$nestingOpen, &$nestingClose) { $data = array(); + $reportLines = array(); $src = explode(PHP_EOL, $bodytext); if ($src === false) { @@ -72,22 +75,27 @@ class BodytextParser { $firstLine = trim($src[0]); - foreach ($src as $row) { + foreach ($src as $key => $row) { $row = trim($row); if ($row === '' || $row[0] === '#') { continue; } $data[] = $row; + + // Increment $key to match line from tt-content record + $key++; + $reportLines[] = $key; } + $this->reportLinesTemp = $reportLines; $this->setNestingToken($firstLine, $nestingOpen, $nestingClose); return implode(PHP_EOL, $data); } /** - * Set the 'nesting token for this tt-conten record. Valid tokens are {}, <>, [], (). + * Set the nesting token for this tt-content record. Valid tokens are {}, <>, [], (). * If the first line of bodytext is a comment line and the last char of that line is a valid token: set that one. * If not: set {} as nesting token. * @@ -170,7 +178,9 @@ class BodytextParser { */ private function joinLine($bodyText, $nestingOpen, $nestingClose) { $data = array(); + $reportLines = $this->reportLinesTemp; $bodytextArray = explode(PHP_EOL, $bodyText); + $firstToken = ''; $nestingOpenRegexp = $nestingOpen; if ($nestingOpen === '(' || $nestingOpen === '[') { @@ -179,7 +189,7 @@ class BodytextParser { $full = ''; $joinDelimiter = ' '; - foreach ($bodytextArray as $row) { + foreach ($bodytextArray as $key => $row) { // Line end with '\'? if (substr($row, -1) == '\\') { @@ -192,8 +202,11 @@ class BodytextParser { if (($row == $nestingOpen || $row == $nestingClose) || (1 === preg_match('/^\d+(\.\d+)*(\s*' . $nestingOpenRegexp . ')?$/', $row)) || (1 === preg_match('/^(\d+\.)*(' . TOKEN_VALID_LIST . ')\s*=/', $row)) - ) { + // Report notation 'alias' + // E.g. myAlias { ... + || (1 === preg_match('/^[\w-]*(\s*' . $nestingOpenRegexp . ')+$/', $row)) + ) { // if there is already something: save this. if ($full !== '') { $data[] = $full; @@ -202,9 +215,26 @@ class BodytextParser { // start new line $full = $row; + // This later determines the notation mode + // Possible values for $firstToken: '10', '10.20', '10.sql=...', '10.head=...', 'myAlias {', 'myAlias{' + // Values such as 'form={{form:SE}}' are valid but not parsed as a level/alias. + if (empty($firstToken) && 1 !== preg_match('/^(' . TOKEN_VALID_LIST . ')\s*=/', $row)) { + $firstToken = (strpos($row, $nestingOpen) !== false) ? trim(substr($row, 0, strpos($row, $nestingOpen))) : $row; + } + + // If the open delimiter is missing while using an alias, this is necessary to get the correct error message later on + // Starts a new line if the previous line only contained '}' + // It prevents that the lines '}' and 'myAlias' will be joined + } elseif ($full === $nestingClose) { + $data[] = $full; + + // start new line + $full = $row; } else { // continue row: concat - the space is necessary to join SQL statements correctly: 'SELECT ... FROM ... WHERE ... AND\np.id=...' - here a 'AND' and 'p.id' need a space. $full .= $joinDelimiter . $row; + // remove unused elements + unset($reportLines[$key]); } $joinDelimiter = $joinDelimiterNext; @@ -215,6 +245,29 @@ class BodytextParser { $data[] = $full; } + $reportLines = array_values($reportLines); + + // Combines line numbers ($key) from tt-content record with content ($value) from corresponding line: [line => content] + // E.g. [0 => 4, 1 => "[", 2 => "sql = SELECT ...", 3 => "[", 4 => "sql = SELECT ...", ...]: line 0 is empty + foreach($reportLines as $key => $value) { + $reportLines[$value] = $data[$key]; + } + + // Removes every element that is not an SQL statement: [line => content] + // E.g. [2 => "sql = SELECT ...", 4 => "sql = SELECT ...", ...] + foreach($reportLines as $key => $value) { + if (strpos($value, '=') !== false) { + $arr = explode('"', $value,2); + if (strpos($arr[0], TOKEN_SQL) === false) { + unset($reportLines[$key]); + } + } else { + unset($reportLines[$key]); + } + } + $this->reportLinesTemp = $reportLines; + $this->firstToken = $firstToken; + return implode(PHP_EOL, $data); } @@ -237,7 +290,14 @@ class BodytextParser { $nestingClose = '\\' . $nestingClose; } - $bodytext = preg_replace('/^((\d+)(\.\d+)*\s*)?(' . $nestingOpen . ')$/m', '$1' . NESTING_TOKEN_OPEN, $bodytext); + // Report notation 'numeric' + // E.g. 10 { ... + // $bodytext = preg_replace('/^((\d+)(\.\d+)*\s*)?(' . $nestingOpen . ')$/m', '$1' . NESTING_TOKEN_OPEN, $bodytext); + + // Report notation 'alias' + // E.g. myAlias { ... + $bodytext = preg_replace('/^((\s*[\w-]*\s*)|((\s*\d+)(\.\d+)*\s*))?(' . $nestingOpen . ')/m', '$1' . NESTING_TOKEN_OPEN, $bodytext); + $bodytext = preg_replace('/^' . $nestingClose . '$/m', '$1' . NESTING_TOKEN_CLOSE, $bodytext); return $bodytext; @@ -277,6 +337,28 @@ class BodytextParser { $result = $bodytext; $posFirstClose = strpos($result, NESTING_TOKEN_CLOSE); + // Default: Report notation 'numeric' + $notationMode = TOKEN_NOTATION_NUMERIC; + $levels = null; + $reportLines = $this->reportLinesTemp; + $alias = null; + $aliases = null; + $firstToken = $this->firstToken; + + // No first token or non-numeric first token implies report notation 'alias' + // It supports auto numbering of blocks and aliases + if (empty($firstToken) || (1 !== preg_match('/^([0-9\._-])+$/', $firstToken) && !strpos($firstToken, '='))) { + $notationMode = TOKEN_NOTATION_ALIAS; + $aliases = array(); + $levels = array(); + $maxLevel = substr_count($bodytext, NESTING_TOKEN_CLOSE); + + // Generate an array containing all levels, e.g. [1,2,3,...] + for ($x = 1; $x <= $maxLevel; $x++) { + array_push($levels, $x); + } + } + while ($posFirstClose !== false) { $posMatchOpen = strrpos(substr($result, 0, $posFirstClose), NESTING_TOKEN_OPEN); @@ -285,7 +367,6 @@ class BodytextParser { throw new \UserFormException( json_encode([ERROR_MESSAGE_TO_USER => 'Missing open delimiter', ERROR_MESSAGE_TO_DEVELOPER => "Missing open delimiter: $result"]), ERROR_MISSING_OPEN_DELIMITER); - } $pre = substr($result, 0, $posMatchOpen); @@ -304,11 +385,57 @@ class BodytextParser { $levelStartPos = ($levelStartPos === false) ? 0 : $levelStartPos + 1; // Skip PHP_EOL $level = trim(substr($pre, $levelStartPos)); -// if($level==='') { -// $pre= -// } - // remove 'level' from last line - $pre = substr($pre, 0, $levelStartPos); + + // Report notation 'alias' + // Count open brackets in front of current level + // E.g. current $level = 2, then $index = 1, because there is 1 in front + $index = substr_count($pre, NESTING_TOKEN_OPEN); + + // Check for report notation 'alias' + if($notationMode === TOKEN_NOTATION_ALIAS) { + $aliasLevel = null; + + // $firstToken === $level checks if we are in the 'first loop' + // $adjustLength is used later while extracting a substring and has to be zero in the first loop + $adjustLength = ($firstToken === $level && strpos($pre, PHP_EOL) === false) ? 0 : 1; + + // If the $level, from which the $alias is extracted, nothing gets saved + // Allowed characters: [a-zA-Z0-9\._-] + // '.' is only allowed to detect report notation 'numeric'. This throws an error later on. + $alias = (1 === preg_match('/^[a-zA-Z0-9\._-]+$/', $level) || $level === '') ? $level : null; + + // If no alias is set, then nothing gets saved + if (!empty($alias)) { + // Construct absolute $level of the current $alias + // E.g. 1.2.3. + for ($x = 0; $x <= $index; $x++) { + $aliasLevel .= (isset($levels[$x])) ? $levels[$x] . '.' : null; + } + + // Trailing '.' gets removed from $level: E.g. 1.2.3 + // $level is saved together with $alias: [level => alias] + $aliases[substr($aliasLevel, 0, strlen($aliasLevel) - 1)] = $alias; + } + + // Current $level can now be extracted from $levels [1,2,3,...] + $level = (isset($levels[$index])) ? $levels[$index] : null; + + // Remove current $level from $levels [1,3,...] + // This works because opening brackets get removed from $pre after every level + unset($levels[$index]); + + // Reset keys + $levels = array_values($levels); + + // Removes alias or level added by user to continue auto numbering scheme + // E.g. User input: {\nsql = SELECT ...\n}\nmyAlias{\nsql = SELECT ...\n} + // $pre = "1.sql = SELECT ...\nmyAlias" -> $pre = "1.sql = SELECT ...\n" + $pre = substr($pre,0, strrpos($pre, PHP_EOL) + $adjustLength); + } else { + + // Remove 'level' from last line + $pre = substr($pre, 0, $levelStartPos); + } // Split nested content in single rows $lines = explode(PHP_EOL, $match); @@ -326,6 +453,33 @@ class BodytextParser { // $result = str_replace('#&]_#', '}', $result); // $result = Support::decryptDoubleCurlyBraces($result); + $resultArr = explode(PHP_EOL, $result); + + // $value (previously SQL statement) gets replaced by its level: [line => level] + // E.g. [2 => 1, 4 => 1.2, ...]: + foreach ($reportLines as $keyLines => $valueLines) { + foreach ($resultArr as $keyResult => $valueResult) { + if (strpos($valueResult, '=')) { + $arr = explode("=", $valueResult, 2); + if (strpos($arr[0], TOKEN_SQL) !== false) { + $reportLines[$keyLines] = str_replace('.' . TOKEN_SQL , '', trim($arr[0])); + } else { + continue; + } + } else { + continue; + } + unset($resultArr[$keyResult]); + break; + } + } + + // Array is flipped: [level => line] + // E.g. [1 => 2, 1.2 => 4, ...] + $this->reportLines = array_flip($reportLines); + + $this->aliases = $aliases; + return $result; } diff --git a/extension/Classes/Core/Constants.php b/extension/Classes/Core/Constants.php index 45db92060a4cb19d934f559f00758e9ebc86b1c1..5f4f2e5c5b7321148c89eac2c7ec74ebdd855275 100644 --- a/extension/Classes/Core/Constants.php +++ b/extension/Classes/Core/Constants.php @@ -254,6 +254,7 @@ const ERROR_INVALID_SAVE_PDF_FILENAME = 1410; const ERROR_TWIG_COLUMN_NOT_UNIQUE = 1411; const ERROR_DOUBLE_DEFINITION = 1412; const ERROR_INVALID_SAVE_ZIP_FILENAME = 1413; +const ERROR_NUMERIC_ALIAS = 1414; // Upload const ERROR_UPLOAD = 1500; @@ -523,6 +524,7 @@ const TYPO3_PAGE_DESCRIPTION = 'pageDescription'; const TYPO3_PAGE_KEYWORDS = 'pageKeywords'; const TYPO3_PAGE_NAV_TITLE = 'pageNavTitle'; const TYPO3_VERSION = 't3Version'; +const TYPO3_TOKEN_REPORT_LINE = 'parsed'; const TYPO3_PAGE_LANGUAGE = SESSION_PAGE_LANGUAGE; const TYPO3_PAGE_LANGUAGE_PATH = SESSION_PAGE_LANGUAGE_PATH; @@ -771,6 +773,7 @@ const SYSTEM_REPORT_COLUMN_VALUE = 'reportColumnValue'; // Value of SQL-column p const SYSTEM_REPORT_FULL_LEVEL = 'reportFullLevel'; // Full level of current report row. E.g.: 10.20.1. Used for error reports. const SYSTEM_MESSAGE_DEBUG = 'messageDebug'; const SYSTEM_DOWNLOAD_POPUP = 'hasDownloadPopup'; // Marker which is set to 'true' if there is at least one Download Link rendered +const SYSTEM_REPORT_LINE = 'reportLine'; const DOWNLOAD_POPUP_REQUEST = 'true'; const DOWNLOAD_POPUP_REPLACE_TEXT = '#downloadPopupReplaceText#'; const DOWNLOAD_POPUP_REPLACE_TITLE = '#downloadPopupReplaceTitle#'; @@ -1766,6 +1769,9 @@ const TOKEN_DB_INDEX = F_DB_INDEX; const TOKEN_DB_INDEX_LC = 'dbindex'; const TOKEN_CONTENT = 'content'; const TOKEN_REPORT_FILE = 'file'; +const TOKEN_ALIAS = 'alias'; +const TOKEN_NOTATION_NUMERIC = 'numeric'; +const TOKEN_NOTATION_ALIAS = 'alias'; const TOKEN_VALID_LIST = 'sql|function|twig|head|althead|altsql|tail|shead|stail|rbeg|rend|renr|rsep|fbeg|fend|fsep|fskipwrap|rbgd|debug|form|r|debugShowBodyText|dbIndex|sqlLog|sqlLogMode|content|render'; @@ -2194,6 +2200,7 @@ const EXCEPTION_REPORT_COLUMN_INDEX = 'Report column index'; // Keyname of SQL-c const EXCEPTION_REPORT_COLUMN_NAME = 'Report column name'; // Keyname of SQL-column processed at the moment. const EXCEPTION_REPORT_COLUMN_VALUE = 'Report column value'; // Keyname of SQL-column processed at the moment. const EXCEPTION_REPORT_FULL_LEVEL = 'Report level key'; +const EXCEPTION_REPORT_LINE = 'Report line'; const EXCEPTION_SIP = 'current sip'; const EXCEPTION_PAGE_ID = 'Page Id'; diff --git a/extension/Classes/Core/Exception/DbException.php b/extension/Classes/Core/Exception/DbException.php index 9ee26dec9f50df5b6515a4db06084356f6fe9d63..4eb1d7b8154b1631faada0d0ef4286a04af27fff 100644 --- a/extension/Classes/Core/Exception/DbException.php +++ b/extension/Classes/Core/Exception/DbException.php @@ -61,6 +61,7 @@ class DbException extends AbstractException { $this->messageArrayDebug[EXCEPTION_SQL_FINAL] = Store::getVar(SYSTEM_SQL_FINAL, STORE_SYSTEM); $this->messageArrayDebug[EXCEPTION_SQL_PARAM_ARRAY] = Store::getVar(SYSTEM_SQL_PARAM_ARRAY, STORE_SYSTEM); $this->messageArrayDebug[EXCEPTION_REPORT_FULL_LEVEL] = Store::getVar(SYSTEM_REPORT_FULL_LEVEL, STORE_SYSTEM); + $this->messageArrayDebug[EXCEPTION_REPORT_LINE] = Store::getVar(SYSTEM_REPORT_LINE, STORE_SYSTEM); return parent::formatException(); } diff --git a/extension/Classes/Core/QuickFormQuery.php b/extension/Classes/Core/QuickFormQuery.php index 643e13fc8db07b3336cc5597b2aa29a1b2de934e..0c89d7571a11223d7588d6427b9fa8789138f5e0 100644 --- a/extension/Classes/Core/QuickFormQuery.php +++ b/extension/Classes/Core/QuickFormQuery.php @@ -172,6 +172,20 @@ class QuickFormQuery { $this->store->setVar(TYPO3_TT_CONTENT_UID, $t3data[T3DATA_UID], STORE_TYPO3); $this->store->setVar(TYPO3_TT_CONTENT_SUBHEADER, $t3data[T3DATA_SUBHEADER], STORE_TYPO3); + // Adds line numbers together with level to TYPO3 store + // E.g. [parsed.1 => 2, parsed.1.2 => 4, ...] + foreach ($btp->reportLines as $key => $value) { + $this->store->setVar(TYPO3_TOKEN_REPORT_LINE . '.' . $key, $value, STORE_TYPO3); + } + + // Check if aliases were used + if (isset($btp->aliases)) { + // Adds aliases together with level to TYPO3 store + // E.g. [alias.1 => "myAlias", alias.1.2 => "mySecondAlias", ...] + foreach ($btp->aliases as $key => $value) { + $this->store->setVar(TOKEN_ALIAS. '.' . $key, $value, STORE_TYPO3); + } + } $this->dbIndexData = $this->store->getVar(SYSTEM_DB_INDEX_DATA, STORE_SYSTEM); $this->dbIndexQfq = $this->store->getVar(SYSTEM_DB_INDEX_QFQ, STORE_SYSTEM); diff --git a/extension/Classes/Core/Report/Report.php b/extension/Classes/Core/Report/Report.php index c5dad53c847bcc7ace1a5be6d0b1507dcf12cfe1..332b4c614c99065e6ccb0c6159252c80eef67cca 100644 --- a/extension/Classes/Core/Report/Report.php +++ b/extension/Classes/Core/Report/Report.php @@ -323,6 +323,27 @@ class Report { if (!empty($this->frArray[$index])) { throw new \UserReportException ("Double definition: $index is defined more than once.", ERROR_DOUBLE_DEFINITION); } + + $alias = TOKEN_ALIAS . "." . $level; + $alias = $this->store->getVar($alias, STORE_TYPO3); + + // Throw exception if alias is numeric + // E.g. 10, 10.20 + if (1 === preg_match('/^([0-9\.])+$/', $alias)) { + throw new \UserReportException ("Numeric alias detected: $alias cannot be used in report notation 'alias'", ERROR_NUMERIC_ALIAS); + } + + // Throw exception if this alias was already used + if (!empty($alias)) { + + // Checks if this alias was already used by a different level + if (!empty($this->aliases) && in_array($alias, $this->aliases) && array_search($alias, $this->aliases) != $level) { + throw new \UserReportException ("Double definition: $alias is defined more than once.", ERROR_DOUBLE_DEFINITION); + } else { + $this->aliases[$level] = $alias; + } + } + // store complete line reformatted in frArray $this->frArray[$index] = $value; @@ -575,9 +596,13 @@ class Report { // Set debug, if one is specified else keep the parent one. $lineDebug = $this->getValueParentDefault(TOKEN_DEBUG, $full_super_level, $fullLevel, $cur_level, 0); + // Get line number of current SQL statement from TYPO3 store + $reportLine = $this->store->getVar(TYPO3_TOKEN_REPORT_LINE . '.' . $fullLevel, STORE_TYPO3); + // Prepare Error reporting $this->store->setVar(SYSTEM_SQL_RAW, $this->frArray[$fullLevel . "." . TOKEN_SQL], STORE_SYSTEM); $this->store->setVar(SYSTEM_REPORT_FULL_LEVEL, $fullLevel, STORE_SYSTEM); + $this->store->setVar(SYSTEM_REPORT_LINE, $reportLine, STORE_SYSTEM); // Prepare SQL: replace variables. Actual 'line.total' or 'line.count' will recalculated: don't replace them now! unset($this->variables->resultArray[$fullLevel . ".line."][LINE_TOTAL]); diff --git a/extension/Classes/Core/Report/Variables.php b/extension/Classes/Core/Report/Variables.php index 834614f3fc08d8e3a8ed1fd575ac74a18bbb2b36..8a15f04da39752aab3f8c87529dd249ba013fd55 100644 --- a/extension/Classes/Core/Report/Variables.php +++ b/extension/Classes/Core/Report/Variables.php @@ -25,6 +25,7 @@ namespace IMATHUZH\Qfq\Core\Report; use IMATHUZH\Qfq\Core\Evaluate; use IMATHUZH\Qfq\Core\Helper\OnString; +use IMATHUZH\Qfq\Core\Store\Store; use IMATHUZH\Qfq\Core\Store\T3Info; @@ -78,7 +79,14 @@ class Variables { // Process all {{x[.x].name}} // $str = preg_replace_callback('/{{\s*(([0-9]+.)+[a-zA-Z0-9_.]+)\s*}}/', 'self::replaceVariables', $text); // $str = preg_replace_callback('/{{\s*(([0-9]+.)+[a-zA-Z0-9_.]+)(:.*)*\s*}}/', 'self::replaceVariables', $text); - $str = preg_replace_callback('/{{\s*(([0-9]+.)+[a-zA-Z0-9_.]+)(:[a-zA-Z-]*)*\s*}}/', 'self::replaceVariables', $text); + + // Report notation 'numeric' + // E.g. {{10.line.count}}, {{10.20.line.count}} + // $str = preg_replace_callback('/{{\s*(([0-9]+.)+[a-zA-Z0-9_.]+)(:[a-zA-Z-]*)*\s*}}/', 'self::replaceVariables', $text); + + // Report notation 'alias' + // E.g. {{myAlias.line.count}}, {{myAlias10.line.count}}, {{10myAlias.line.count}} + $str = preg_replace_callback('/{{\s*(([a-zA-Z0-9_-]*[0-9.]*.)[.][a-zA-Z0-9_.]+)+(:[a-zA-Z-]*)*\s*}}/', 'self::replaceVariables', $text); // Try the Stores return $this->eval->parse($str, null, null, $dummyStack, $dummyStore, $frCmd); @@ -96,12 +104,41 @@ class Variables { */ public function replaceVariables($matches): string { - // $matches[0]: {{10.20.<columnname>::u:}} - // $matches[1]: 10.20.<columnname> - // $matches[2]: 10.20 - // $matches[3]: ::u: + // Report notation 'numeric + // $matches[0]: {{10.20.<columnname>::u}} + // $matches[1]: 10.20.<columnname> + // $matches[2]: 10.20 + // $matches[3]: :u + + // Report notation 'alias' + // $matches[0]: {{myAlias.<columnname>::u}} + // $matches[1]: myAlias.<columnname> + // $matches[2]: myAlias + // $matches[3]: :u $data = $matches[0]; + // Isolate first token as possible alias + $alias = strtok($matches[2], "."); + + // No numeric value implies that an alias was used + if (!is_numeric($alias)) { + // Get typo3 store + // Aliases are saved like [alias.1 => "myAlias", alias.1.2 => "mySecondAlias", ...] + $storeT3 = Store::getStore(STORE_TYPO3); + // Check for matching value of $alias + $match = array_search($alias, $storeT3, true); + + // Replacement only if matching alias was found + if (!empty($match)) { + // Extract level from key + $level = substr($match, strpos($match, '.') + 1); + + $matches[0] = str_replace($alias, $level, $matches[0]); + $matches[1] = str_replace($alias, $level, $matches[1]); + $matches[2] = $level . '.'; + } + } + // index of last '.' $pos = strrpos($matches[1], "."); if ($pos !== false) { @@ -116,6 +153,15 @@ class Variables { $arr = explode(':', $matches[3]); $data = OnString::escape($arr[1] ?? '', $this->resultArray[$fullLevel][$varName], $rcFlagWipe); } + // This is for the specific case, that the variable references its own level + // E.g. myAlias { \n sql = SELECT '{{myAlias.line.count}}' \n } + // Note: This is only used for line.count and line.total, because non-existing variables must stay unchanged + // E.g. myAlias { \n sql = SELECT 1 \n } \n { \n sql = SELECT '{{myAlias.varName}}' \n } + // '{{myAlias.varName}}' will not be changed to '{{1.varName}}' + } elseif($varName === LINE_COUNT || $varName === LINE_TOTAL) { + // myAlias needs to be replaced by the level + // E.g. {{1.2.line.count}} + $data = $matches[0]; } } diff --git a/extension/Tests/Unit/Core/BodytextParserTest.php b/extension/Tests/Unit/Core/BodytextParserTest.php index b069ba46657ff45d209644b50d5d7ef48c448d62..09487b62e073748079d053840bc378b20a88078c 100644 --- a/extension/Tests/Unit/Core/BodytextParserTest.php +++ b/extension/Tests/Unit/Core/BodytextParserTest.php @@ -27,12 +27,40 @@ final class BodytextParserTest extends TestCase { $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Simple row, nothing to remove + $given = "{\nsql = SELECT 'Hello World'\n}"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Simple row, nothing to remove + $given = "myAlias {\nsql = SELECT 'Hello World'\n}"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Several rows, remove all but one $given = "\n#some comments\n10.sql = SELECT 'Hello World'\n\n \n #more comment"; $expected = "10.sql = SELECT 'Hello World'"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Several rows, remove all but one + $given = "\n#some comments\n{\nsql = SELECT 'Hello World'\n\n \n #more comment\n}"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Several rows, remove all but one + $given = "\n#some comments\nmyAlias {\nsql = SELECT 'Hello World'\n\n \n #more comment\n}"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Several rows, all to remove $given = "\n#some comments\n\n\n \n #more comment"; $expected = ""; @@ -45,66 +73,220 @@ final class BodytextParserTest extends TestCase { $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Join a line + $given = "\n{\nsql = SELECT 'Hello World',\n'more content'\n WHERE help=1\n}"; + $expected = "1.sql = SELECT 'Hello World', 'more content' WHERE help=1"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Join a line + $given = "\n myAlias {\nsql = SELECT 'Hello World',\n'more content'\n WHERE help=1\n}"; + $expected = "1.sql = SELECT 'Hello World', 'more content' WHERE help=1"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Join several lines, incl. form $given = "\n10.sql = SELECT 'Hello World',\n'more content'\n WHERE help=1\n 20.head = <table>\n 30.sql = SELECT\n col1,\n col2, \n col3\n # Query stops here\nform = Person\n"; $expected = "10.sql = SELECT 'Hello World', 'more content' WHERE help=1\n20.head = <table>\n30.sql = SELECT col1, col2, col3\nform = Person"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Join several lines, incl. form + $given = "\n{\nsql = SELECT 'Hello World',\n'more content'\n WHERE help=1\n}\n{\nhead = <table>\n}\n{\nsql = SELECT\n col1,\n col2, \n col3\n # Query stops here\n}\nform = Person\n"; + $expected = "1.sql = SELECT 'Hello World', 'more content' WHERE help=1\n2.head = <table>\n3.sql = SELECT col1, col2, col3\nform = Person"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Join several lines, incl. form + $given = "\nmyAlias {\nsql = SELECT 'Hello World',\n'more content'\n WHERE help=1\n}\n mySecondAlias {\nhead = <table>\n}\nmyThirdAlias {\nsql = SELECT\n col1,\n col2, \n col3\n # Query stops here\n}\nform = Person\n"; + $expected = "1.sql = SELECT 'Hello World', 'more content' WHERE help=1\n2.head = <table>\n3.sql = SELECT col1, col2, col3\nform = Person"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Nested expression: one. $given = "10{\nsql = SELECT 'Hello World'\n}\n"; $expected = "10.sql = SELECT 'Hello World'"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Nested expression: one. + $given = "{\nsql = SELECT 'Hello World'\n}\n"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Nested expression: one. + $given = "myAlias {\nsql = SELECT 'Hello World'\n}\n"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Nested expression: one. No LF at the end $given = "10{\nsql = SELECT 'Hello World'\n}"; $expected = "10.sql = SELECT 'Hello World'"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Nested expression: one. No LF at the end + $given = "{\nsql = SELECT 'Hello World'\n}"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Nested expression: one. No LF at the end + $given = "myAlias {\nsql = SELECT 'Hello World'\n}"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Nested expression, one, added some white spaces $given = "\n\n10 { \n \n sql = SELECT 'Hello World' \n\n }\n\n"; $expected = "10.sql = SELECT 'Hello World'"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Nested expression, one, added some white spaces + $given = "\n\n { \n \n sql = SELECT 'Hello World' \n\n }\n\n"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Nested expression, one, added some white spaces + $given = "\n\n myAlias { \n \n sql = SELECT 'Hello World' \n\n }\n\n"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Nested expression: multiple, simple $given = "10.sql = SELECT 'Hello World'\n20 {\nsql='Hello world2'\n}\n30 {\nsql='Hello world3'\n}\n"; $expected = "10.sql = SELECT 'Hello World'\n20.sql='Hello world2'\n30.sql='Hello world3'"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Nested expression: multiple, simple + $given = "{\nsql = SELECT 'Hello World'\n}\n{\nsql='Hello world2'\n}\n {\nsql='Hello world3'\n}\n"; + $expected = "1.sql = SELECT 'Hello World'\n2.sql='Hello world2'\n3.sql='Hello world3'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Nested expression: multiple, simple + $given = "myAlias {\nsql = SELECT 'Hello World'\n}\nmyAlias10{\nsql='Hello world2'\n}\n 10myAlias{\nsql='Hello world3'\n}\n"; + $expected = "1.sql = SELECT 'Hello World'\n2.sql='Hello world2'\n3.sql='Hello world3'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Nested expression: complex $given = "10.sql = SELECT 'Hello World'\n20 {\nsql='Hello world2'\n30 { \n sql=SELECT 'Hello World3'\n40 { \n sql = SELECT 'Hello World4'\n } \n } \n } "; $expected = "10.sql = SELECT 'Hello World'\n20.sql='Hello world2'\n20.30.sql=SELECT 'Hello World3'\n20.30.40.sql = SELECT 'Hello World4'"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Nested expression: complex + $given = "{\nsql = SELECT 'Hello World'\n}\n {\nsql='Hello world2'\n { \n sql=SELECT 'Hello World3'\n { \n sql = SELECT 'Hello World4'\n } \n } \n } "; + $expected = "1.sql = SELECT 'Hello World'\n2.sql='Hello world2'\n2.3.sql=SELECT 'Hello World3'\n2.3.4.sql = SELECT 'Hello World4'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Nested expression: complex + $given = "myAlias{\nsql = SELECT 'Hello World'\n}\n myAlias2{\nsql='Hello world2'\n myAlias3{ \n sql=SELECT 'Hello World3'\n myAlias4{ \n sql = SELECT 'Hello World4'\n } \n } \n } "; + $expected = "1.sql = SELECT 'Hello World'\n2.sql='Hello world2'\n2.3.sql=SELECT 'Hello World3'\n2.3.4.sql = SELECT 'Hello World4'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // form=...., {{ }} $given = "10.sql = SELECT 'Hello World'\nform = {{form:S}}\n20.sql = SELECT 'Hello World2'\n30 {\nsql=SELECT 'Hello World'\n}\n form=Person\n"; $expected = "10.sql = SELECT 'Hello World'\nform = {{form:S}}\n20.sql = SELECT 'Hello World2'\n30.sql=SELECT 'Hello World'\nform=Person"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // form=...., {{ }} + $given = "{\nsql = SELECT 'Hello World'\n}\nform = {{form:S}}\n{\nsql = SELECT 'Hello World2'\n}\n {\nsql=SELECT 'Hello World'\n}\n form=Person\n"; + $expected = "1.sql = SELECT 'Hello World'\nform = {{form:S}}\n2.sql = SELECT 'Hello World2'\n3.sql=SELECT 'Hello World'\nform=Person"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // form=...., {{ }} + $given = "myAlias{\nsql = SELECT 'Hello World'\n}\nform = {{form:S}}\nmyAlias2 {\nsql = SELECT 'Hello World2'\n}\n myAlias3{\nsql=SELECT 'Hello World'\n}\n form=Person\n"; + $expected = "1.sql = SELECT 'Hello World'\nform = {{form:S}}\n2.sql = SELECT 'Hello World2'\n3.sql=SELECT 'Hello World'\nform=Person"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Nested: open bracket alone $given = "10.sql = SELECT 'Hello World'\n20\n{\nhead=test\n}\n30.sql = SELECT 'Hello World'\n"; $expected = "10.sql = SELECT 'Hello World'\n20.head=test\n30.sql = SELECT 'Hello World'"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Nested: open bracket alone + $given = "{\nsql = SELECT 'Hello World'\n}\n\n{\nhead=test\n}\n{\nsql = SELECT 'Hello World'\n}"; + $expected = "1.sql = SELECT 'Hello World'\n2.head=test\n3.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Nested: open bracket alone + $given = "myAlias{\nsql = SELECT 'Hello World'\n}\n\nmySecondAlias{\nhead=test\n}\nmyThirdAlias{\nsql = SELECT 'Hello World'\n}"; + $expected = "1.sql = SELECT 'Hello World'\n2.head=test\n3.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Single open bracket inside a string. $given = "10.sql = SELECT 'Hello { World'"; $expected = "10.sql = SELECT 'Hello { World'"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Single open bracket inside a string. + $given = "{\nsql = SELECT 'Hello { World'\n}"; + $expected = "1.sql = SELECT 'Hello { World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Single open bracket inside a string. + $given = "myAlias{\nsql = SELECT 'Hello { World'\n}"; + $expected = "1.sql = SELECT 'Hello { World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Complex test $given = "10.sql = SELECT '[\*]{7} [0-9]{5}<br>'\n20 {\n 10 {\n 5 {\n sql = SELECT 'hello world<br>'\n }\n }\n}\n20.10.5.head = Terific\n20.sql = SELECT 20, '<br>'\n20.10.sql = SELECT '20.10<br>'"; $expected = "10.sql = SELECT '[\*]{7} [0-9]{5}<br>'\n20.10.5.sql = SELECT 'hello world<br>'\n20.10.5.head = Terific\n20.sql = SELECT 20, '<br>'\n20.10.sql = SELECT '20.10<br>'"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Complex test + $given = "{\nsql = SELECT '[\*]{7} [0-9]{5}<br>'\n}\n{\n {\n {\n sql = SELECT 'hello world<br>'\n head = Terrific \n}\n }\n}\n{\nsql = SELECT 5, '<br>'\n}\n{\nsql = SELECT '6<br>'\n}"; + $expected = "1.sql = SELECT '[\*]{7} [0-9]{5}<br>'\n2.3.4.sql = SELECT 'hello world<br>'\n2.3.4.head = Terrific\n5.sql = SELECT 5, '<br>'\n6.sql = SELECT '6<br>'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Complex test + $given = "myAliasOne{\nsql = SELECT '[\*]{7} [0-9]{5}<br>'\n}\nmyAliasTwo{\n myAliasThree{\n myAliasFour{\n sql = SELECT 'hello world<br>'\n head = Terrific \n}\n }\n}\nmyAliasFive {\nsql = SELECT 5, '<br>'\n}\nmyLastAlias{\nsql = SELECT '6<br>'\n}"; + $expected = "1.sql = SELECT '[\*]{7} [0-9]{5}<br>'\n2.3.4.sql = SELECT 'hello world<br>'\n2.3.4.head = Terrific\n5.sql = SELECT 5, '<br>'\n6.sql = SELECT '6<br>'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + } public function testNestingToken() { @@ -116,48 +298,160 @@ final class BodytextParserTest extends TestCase { $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Nested expression: one level curly + $given = "{ \n sql = SELECT 'Hello World' \n , 'next line', \n \n 'end' \n} \n"; + $expected = "1.sql = SELECT 'Hello World' , 'next line', 'end'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Nested expression: one level curly + $given = "myAlias{ \n sql = SELECT 'Hello World' \n , 'next line', \n \n 'end' \n} \n"; + $expected = "1.sql = SELECT 'Hello World' , 'next line', 'end'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Nested expression: one level angle $given = "#<\n10 < \n sql = SELECT 'Hello World'\n>\n"; $expected = "10.sql = SELECT 'Hello World'"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Nested expression: one level angle + $given = "#<\n < \n sql = SELECT 'Hello World'\n>\n"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Nested expression: one level angle + $given = "#<\n myAlias< \n sql = SELECT 'Hello World'\n>\n"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Nested expression: one level angle and single curly $given = "#<\n10 < \n head = data { \n '1','2','3' \n }\n>\n"; $expected = "10.head = data { '1','2','3' }"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Nested expression: one level angle and single curly + $given = "#<\n < \n head = data { \n '1','2','3' \n }\n>\n"; + $expected = "1.head = data { '1','2','3' }"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Nested expression: one level angle and single curly + $given = "#<\n myAlias< \n head = data { \n '1','2','3' \n }\n>\n"; + $expected = "1.head = data { '1','2','3' }"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Nested expression: one level angle and single curly $given = " # < \n 10 < \n sql = SELECT 'Hello World' \n>\n"; $expected = "10.sql = SELECT 'Hello World'"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Nested expression: one level angle and single curly + $given = " # < \n < \n sql = SELECT 'Hello World' \n>\n"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Nested expression: one level angle and single curly + $given = " # myAlias1< \n myAlias2< \n sql = SELECT 'Hello World' \n>\n"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Nested expression: one level round bracket $given = " # ( \n 10 ( \n sql = SELECT 'Hello World' \n)\n"; $expected = "10.sql = SELECT 'Hello World'"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Nested expression: one level round bracket + $given = " # ( \n ( \n sql = SELECT 'Hello World' \n)\n"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Nested expression: one level round bracket + $given = " # ( \n myAlias( \n sql = SELECT 'Hello World' \n)\n"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Nested expression: one level square bracket $given = " # [ \n 10 [ \n sql = SELECT 'Hello World' \n]\n"; $expected = "10.sql = SELECT 'Hello World'"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Nested expression: one level square bracket + $given = " # [ \n [ \n sql = SELECT 'Hello World' \n]\n"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Nested expression: one level square bracket + $given = " # [ \n myAlias[ \n sql = SELECT 'Hello World' \n]\n"; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Nested expression: one level angle - garbage $given = " # < \n 10 { \n sql = SELECT 'Hello World' \n}\n"; $expected = "10 {\nsql = SELECT 'Hello World' }"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Nested expression: one level angle - garbage + $given = " # < \n { \n sql = SELECT 'Hello World' \n}\n"; + $expected = "{\nsql = SELECT 'Hello World' }"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Nested expression: one level angle - garbage + $given = " # < \n myAlias{ \n sql = SELECT 'Hello World' \n}\n"; + $expected = "myAlias{\nsql = SELECT 'Hello World' }"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // Nested expression: '</script>' is allowed here - with bad implemented parsing, it would detect a nesting token end, which is not the meant. $given = " # < \n 10 < \n sql = SELECT 'Hello World' \n head = <script> \n>\n20.tail=</script>\n\n\n30.sql=SELECT 'something'"; $expected = "10.sql = SELECT 'Hello World'\n10.head = <script>\n20.tail=</script>\n30.sql=SELECT 'something'"; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // Nested expression: '</script>' is allowed here - with bad implemented parsing, it would detect a nesting token end, which is not the meant. + $given = " # < \n < \n sql = SELECT 'Hello World' \n head = <script> \n>\n<\ntail=</script>\n>\n<\nsql=SELECT 'something'\n>"; + $expected = "1.sql = SELECT 'Hello World'\n1.head = <script>\n2.tail=</script>\n3.sql=SELECT 'something'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Nested expression: '</script>' is allowed here - with bad implemented parsing, it would detect a nesting token end, which is not the meant. + $given = " # < \n myAlias < \n sql = SELECT 'Hello World' \n head = <script> \n>\n myAlias2 <\ntail=</script>\n>\nmyAlias3<\nsql=SELECT 'something'\n>"; + $expected = "1.sql = SELECT 'Hello World'\n1.head = <script>\n2.tail=</script>\n3.sql=SELECT 'something'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + $open = '<'; $close = '>'; // muliple nesting, unnested rows inbetween @@ -211,6 +505,125 @@ EOF; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // muliple nesting, unnested rows inbetween + $given = <<<EOF + # $open + + $open + head = <h1> + $close + $open + sql = SELECT ... + $close + $open + $open + sql = SELECT 3.4 + head = <script> + tail = </script> + $open + head = <div> + $close + $close + $open + sql = SELECT 3.6 + $close + $open + sql = SELECT 3.7 + $close + $open + $open + tail = } + } + (: + } + ] + sql = SELECT 3.8.9 + head = { + { + ) + { + ; + [ + $close + $open + sql = SELECT 3.8.10 + $close + $open + sql = SELECT 3.8.11 + $close + $close + $open + head = <table> + $close + $close + $open + sql = SELECT 13 + $close +EOF; + $expected = "1.head = <h1>\n2.sql = SELECT ...\n3.4.sql = SELECT 3.4\n3.4.head = <script>\n3.4.tail = </script>\n3.4.5.head = <div>\n3.6.sql = SELECT 3.6\n3.7.sql = SELECT 3.7\n3.8.9.tail = } } (: } ]\n3.8.9.sql = SELECT 3.8.9\n3.8.9.head = { { ) { ; [\n3.8.10.sql = SELECT 3.8.10\n3.8.11.sql = SELECT 3.8.11\n3.12.head = <table>\n13.sql = SELECT 13"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // muliple nesting, unnested rows inbetween + $given = <<<EOF + # $open + + myAlias1 $open + head = <h1> + $close + myAlias2 $open + sql = SELECT ... + $close + myAlias3 $open + myAlias4 $open + sql = SELECT 3.4 + head = <script> + tail = </script> + myAlias4 $open + head = <div> + $close + $close + myAlias5 $open + sql = SELECT 3.6 + $close + myAlias6 $open + sql = SELECT 3.7 + $close + myAlias7 $open + myAlias8 $open + tail = } + } + (: + } + ] + sql = SELECT 3.8.9 + head = { + { + ) + { + ; + [ + $close + myAlias9 $open + sql = SELECT 3.8.10 + $close + myAlias10$open + sql = SELECT 3.8.11 + $close + $close + myAlias11$open + head = <table> + $close + $close + myAlias12$open + sql = SELECT 13 + $close +EOF; + $expected = "1.head = <h1>\n2.sql = SELECT ...\n3.4.sql = SELECT 3.4\n3.4.head = <script>\n3.4.tail = </script>\n3.4.5.head = <div>\n3.6.sql = SELECT 3.6\n3.7.sql = SELECT 3.7\n3.8.9.tail = } } (: } ]\n3.8.9.sql = SELECT 3.8.9\n3.8.9.head = { { ) { ; [\n3.8.10.sql = SELECT 3.8.10\n3.8.11.sql = SELECT 3.8.11\n3.12.head = <table>\n13.sql = SELECT 13"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); } public function testVariousNestingToken() { @@ -232,6 +645,30 @@ EOF; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // level open + $given = <<<EOF + # $open + $open + sql = SELECT 'Hello World' + $close +EOF; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // level open + $given = <<<EOF + # $open + myAlias $open + sql = SELECT 'Hello World' + $close +EOF; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // level \n alone $given = <<<EOF # $open @@ -244,6 +681,32 @@ EOF; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // level \n alone + $given = <<<EOF + # $open + + $open + sql = SELECT 'Hello World' + $close +EOF; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // level \n alone + $given = <<<EOF + # $open + + myAlias$open + sql = SELECT 'Hello World' + $close +EOF; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // various linebreaks $given = <<<EOF # $open @@ -259,6 +722,38 @@ EOF; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // various linebreaks + $given = <<<EOF + # $open + + $open + + sql = SELECT 'Hello World' + + $close + +EOF; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // various linebreaks + $given = <<<EOF + # $open + + myAlias $open + + sql = SELECT 'Hello World' + + $close + +EOF; + $expected = "1.sql = SELECT 'Hello World'"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // multi line $given = <<<EOF # $open @@ -277,6 +772,42 @@ EOF; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // multi line + $given = <<<EOF + # $open + $open + sql = SELECT 'Hello World' + FROM Person + + ORDER BY id + + LIMIT 4 + head = <div> + $close +EOF; + $expected = "1.sql = SELECT 'Hello World' FROM Person ORDER BY id LIMIT 4\n1.head = <div>"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // multi line + $given = <<<EOF + # $open + myAlias $open + sql = SELECT 'Hello World' + FROM Person + + ORDER BY id + + LIMIT 4 + head = <div> + $close +EOF; + $expected = "1.sql = SELECT 'Hello World' FROM Person ORDER BY id LIMIT 4\n1.head = <div>"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // mulitple nesting $given = <<<EOF # $open @@ -300,6 +831,58 @@ EOF; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // mulitple nesting + $given = <<<EOF + # $open + $open + head = <h1> + $close + $open + sql = SELECT 'Hello World' + $open + sql = SELECT 'Hi' + head = <script> + tail = </script> + $open + head = <div> + $close + + $close + + $close + +EOF; + $expected = "1.head = <h1>\n2.sql = SELECT 'Hello World'\n2.3.sql = SELECT 'Hi'\n2.3.head = <script>\n2.3.tail = </script>\n2.3.4.head = <div>"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // mulitple nesting + $given = <<<EOF + # $open + myAlias$open + head = <h1> + $close + myAlias2$open + sql = SELECT 'Hello World' + myAlias3$open + sql = SELECT 'Hi' + head = <script> + tail = </script> + myAlias4$open + head = <div> + $close + + $close + + $close + +EOF; + $expected = "1.head = <h1>\n2.sql = SELECT 'Hello World'\n2.3.sql = SELECT 'Hi'\n2.3.head = <script>\n2.3.tail = </script>\n2.3.4.head = <div>"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + // muliple nesting, unnested rows inbetween $given = <<<EOF # $open @@ -325,6 +908,66 @@ EOF; $result = $btp->process($given); $this->assertEquals($expected, $result); + // Report notation 'alias' + // muliple nesting, unnested rows inbetween + $given = <<<EOF + # $open + $open + head = <h1> + $close + $open + sql = SELECT 'Hello World' + $open + sql = SELECT 'Hi' + head = <script> + tail = </script> + $open + head = <div> + $close + $close + $open + sql = SELECT 'After' + $close + $close + $open + sql = SELECT ... + $close + +EOF; + $expected = "1.head = <h1>\n2.sql = SELECT 'Hello World'\n2.3.sql = SELECT 'Hi'\n2.3.head = <script>\n2.3.tail = </script>\n2.3.4.head = <div>\n2.5.sql = SELECT 'After'\n6.sql = SELECT ..."; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // muliple nesting, unnested rows inbetween + $given = <<<EOF + # $open + myAlias$open + head = <h1> + $close + mySecondAlias$open + sql = SELECT 'Hello World' + myThirdAlias$open + sql = SELECT 'Hi' + head = <script> + tail = </script> + myFourthAlias$open + head = <div> + $close + $close + myFifthAlias$open + sql = SELECT 'After' + $close + $close + mySixthAlias$open + sql = SELECT ... + $close + +EOF; + $expected = "1.head = <h1>\n2.sql = SELECT 'Hello World'\n2.3.sql = SELECT 'Hi'\n2.3.head = <script>\n2.3.tail = </script>\n2.3.4.head = <div>\n2.5.sql = SELECT 'After'\n6.sql = SELECT ..."; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + } } @@ -347,6 +990,40 @@ EOF; $expected = "10.sql = SELECT 'Hello World', 'p:id=1&grId=2\n20.sql = SELECT ''"; $result = $btp->process($given); $this->assertEquals($expected, $result); + + // Report notation 'alias' + // Simple row, nothing to remove + $given = "{\nsql = SELECT 'Hello World', 'p:id=1&\\\ngrId=2\n}"; + $expected = "1.sql = SELECT 'Hello World', 'p:id=1&grId=2"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + $given = "{\nsql = SELECT 'Hello World', 'p:id=1& \\\n grId=2 \n}"; + $expected = "1.sql = SELECT 'Hello World', 'p:id=1&grId=2"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + $given = "{\nsql = SELECT 'Hello World', 'p:id=1& \\\n grId=2 \\\n }\n{\nsql = SELECT ''\n}"; + $expected = "1.sql = SELECT 'Hello World', 'p:id=1&grId=2\n2.sql = SELECT ''"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Simple row, nothing to remove + $given = "myAlias{\nsql = SELECT 'Hello World', 'p:id=1&\\\ngrId=2\n}"; + $expected = "1.sql = SELECT 'Hello World', 'p:id=1&grId=2"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + $given = "myAlias{\nsql = SELECT 'Hello World', 'p:id=1& \\\n grId=2 \n}"; + $expected = "1.sql = SELECT 'Hello World', 'p:id=1&grId=2"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); + + $given = "myAlias{\nsql = SELECT 'Hello World', 'p:id=1& \\\n grId=2 \\\n }\n{\nsql = SELECT ''\n}"; + $expected = "1.sql = SELECT 'Hello World', 'p:id=1&grId=2\n2.sql = SELECT ''"; + $result = $btp->process($given); + $this->assertEquals($expected, $result); } /** @@ -360,6 +1037,14 @@ EOF; // Nested: unmatched close bracket $btp->process("10.sql = SELECT 'Hello World'\n } \n30.sql = SELECT 'Hello World'\n"); + // Report notation 'alias' + // Nested: unmatched close bracket + $btp->process("sql = SELECT 'Hello World'\n } \n{\nsql = SELECT 'Hello World'\n}"); + + // Report notation 'alias' with alias + // Nested: unmatched close bracket + $btp->process("sql = SELECT 'Hello World'\n } \nmyAlias{\nsql = SELECT 'Hello World'\n}"); + } /** @@ -372,6 +1057,237 @@ EOF; // Nested: unmatched open bracket $btp->process("10.sql = SELECT 'Hello World'\n20 { \n30.sql = SELECT 'Hello World'\n"); + // Report notation 'alias' + // Nested: unmatched open bracket + $btp->process("{\nsql = SELECT 'Hello World'\n}\n { \n30.sql = SELECT 'Hello World'\n"); + + // Report notation 'alias' with alias + // Nested: unmatched open bracket + $btp->process("myAlias{\nsql = SELECT 'Hello World'\n}\n myAlias2{ \n30.sql = SELECT 'Hello World'\n"); + } -} + /** + * + */ + public function testReportLines() { + $btp = new BodytextParser(); + + // Simple statement + $given = "10.sql = SELECT 'Hello World'"; + $expected = [10 => 1]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Simple statement, nested + $given = "10 {\nsql = SELECT 'Hello World'\n}"; + $expected = [10 => 2]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Report notation 'alias' + // Simple statement, nested + $given = "{\nsql = SELECT 'Hello World'\n}"; + $expected = [1 => 2]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Simple statement, nested + $given = "myAlias{\nsql = SELECT 'Hello World'\n}"; + $expected = [1 => 2]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Simple statement, multiple unnecessary lines + $given = "\n#some comments\n10.sql = SELECT 'Hello World'\n\n \n #more comment"; + $expected = [10 => 3]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Simple statement, multiple unnecessary lines, nested + $given = "\n#some comments\n10\n{\nsql = SELECT 'Hello World'\n\n}\n \n #more comment"; + $expected = [10 => 5]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Report notation 'alias' + // Simple statement, multiple unnecessary lines, nested + $given = "\n#some comments\n\n{\nsql = SELECT 'Hello World'\n\n}\n \n #more comment"; + $expected = [1 => 5]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Simple statement, multiple unnecessary lines, nested + $given = "\n#some comments\n\nmyAlias{\nsql = SELECT 'Hello World'\n\n}\n \n #more comment"; + $expected = [1 => 5]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // No statement + $given = "\n#some comments\n\n\n \n #more comment"; + $expected = []; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Simple statement on multiple lines + $given = "\n10.sql = SELECT 'Hello World',\n'more content'\n WHERE help=1"; + $expected = [10 => 2]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Simple statement on multiple lines, nested + $given = "\n10 {\nsql = SELECT 'Hello World',\n'more content'\n WHERE help=1\n}"; + $expected = [10 => 3]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Report notation 'alias' + // Simple statement on multiple lines, nested + $given = "\n {\nsql = SELECT 'Hello World',\n'more content'\n WHERE help=1\n}"; + $expected = [1 => 3]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Simple statement on multiple lines, nested + $given = "\n myAlias{\nsql = SELECT 'Hello World',\n'more content'\n WHERE help=1\n}"; + $expected = [1 => 3]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Simple statements + $given = "10.sql = SELECT 'Hello World'\n20.sql = SELECT 'Hello World'\n30.sql = SELECT 'Hello World'"; + $expected = [10 => 1, 20 => 2, 30 => 3]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Simple statements, nested + $given = "10 {\nsql = SELECT 'Hello World'\n}\n20 {\nsql = SELECT 'Hello World'\n}\n30 {\nsql = SELECT 'Hello World'\n}"; + $expected = [10 => 2, 20 => 5, 30 => 8]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Report notation 'alias' + // Simple statements, nested + $given = "{\nsql = SELECT 'Hello World'\n}\n{\nsql = SELECT 'Hello World'\n}\n{\nsql = SELECT 'Hello World'\n}"; + $expected = [1 => 2, 2 => 5, 3 => 8]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Simple statements, nested + $given = "myAlias{\nsql = SELECT 'Hello World'\n}\nmyAlias2{\nsql = SELECT 'Hello World'\n}\nmyAlias3{\nsql = SELECT 'Hello World'\n}"; + $expected = [1 => 2, 2 => 5, 3 => 8]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Simple statements, multiple unnecessary lines + $given = "\n#some comments\n10.sql = SELECT 'Hello World'\n\n \n #more comment \n 20.sql = SELECT 'Hello World'\n #more comment\n\n\n 30.sql=SELECT 'Hello World'"; + $expected = [10 => 3, 20 => 7, 30 => 11]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Simple statements, multiple unnecessary lines, nested + $given = "\n#some comments\n10\n{\nsql = SELECT 'Hello World'\n\n}\n \n #more comment\n\n 20 {\nsql = SELECT 'Hello World'\n\n}\n #more comment \n\n\n 30 {\nsql = SELECT 'Hello World'\n\n}"; + $expected = [10 => 5, 20 => 12, 30 => 19]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Report notation 'alias' + // Simple statements, multiple unnecessary lines, nested + $given = "\n#some comments\n\n{\nsql = SELECT 'Hello World'\n\n}\n \n #more comment\n\n {\nsql = SELECT 'Hello World'\n\n}\n #more comment \n\n\n {\nsql = SELECT 'Hello World'\n\n}"; + $expected = [1 => 5, 2 => 12, 3 => 19]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Simple statements, multiple unnecessary lines, nested + $given = "\n#some comments\n\nmyAlias{\nsql = SELECT 'Hello World'\n\n}\n \n #more comment\n\n myAlias2{\nsql = SELECT 'Hello World'\n\n}\n #more comment \n\n\n myAlias3{\nsql = SELECT 'Hello World'\n\n}"; + $expected = [1 => 5, 2 => 12, 3 => 19]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Simple statements on multiple lines + $given = "\n10.sql = SELECT 'Hello World',\n'more content'\n WHERE help=1\n\n 20.sql = SELECT 'Hello World', \n 'some more content'"; + $expected = [10 => 2, 20 => 6]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Simple statements on multiple lines, nested + $given = "\n10 {\nsql = SELECT 'Hello World',\n'more content'\n WHERE help=1\n}\n\n20\n {\nsql = SELECT 'Hello World', \n 'some more content'\n}"; + $expected = [10 => 3, 20 => 10]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Report notation 'alias' + // Simple statements on multiple lines, nested + $given = "\n {\nsql = SELECT 'Hello World',\n'more content'\n WHERE help=1\n}\n\n{\nsql = SELECT 'Hello World', \n 'some more content'\n}"; + $expected = [1 => 3, 2 => 9]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Simple statements on multiple lines, nested + $given = "\n myAlias{\nsql = SELECT 'Hello World',\n'more content'\n WHERE help=1\n}\n\nmyAlias2{\nsql = SELECT 'Hello World', \n 'some more content'\n}"; + $expected = [1 => 3, 2 => 9]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Complex statements + $given = "10.sql = SELECT 'Hello World'\n20.sql='Hello world2'\n20.30.sql=SELECT 'Hello World3'\n20.30.40.sql = SELECT 'Hello World4'"; + $expected = [10 => 1, 20 => 2, '20.30' => 3, '20.30.40' => 4]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Complex statements, nested + $given = "10.sql = SELECT 'Hello World'\n20 {\nsql='Hello world2'\n30 { \n sql=SELECT 'Hello World3'\n40 { \n sql = SELECT 'Hello World4'\n } \n } \n } "; + $expected = [10 => 1, 20 => 3, '20.30' => 5, '20.30.40' => 7]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Report notation 'alias' + // Complex statements: complex + $given = "{\nsql = SELECT 'Hello World'\n}\n {\nsql='Hello world2'\n { \n sql=SELECT 'Hello World3'\n { \n sql = SELECT 'Hello World4'\n } \n } \n } "; + $expected = [1 => 2, 2 => 5, '2.3' => 7, '2.3.4' => 9]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + + // Report notation 'alias' with alias + // Complex statements: complex + $given = "myAlias{\nsql = SELECT 'Hello World'\n}\n myAlias2{\nsql='Hello world2'\n myAlias3{ \n sql=SELECT 'Hello World3'\n myAlias4{ \n sql = SELECT 'Hello World4'\n } \n } \n } "; + $expected = [1 => 2, 2 => 5, '2.3' => 7, '2.3.4' => 9]; + $btp->process($given); + $result = $btp->reportLines; + $this->assertEquals($expected, $result); + } +} \ No newline at end of file diff --git a/extension/Tests/Unit/Core/Report/ReportTest.php b/extension/Tests/Unit/Core/Report/ReportTest.php index 789b04a23d134f5480adf434c6ab768b05d85644..b8135a028bc918d113e21ada43283591505233c9 100644 --- a/extension/Tests/Unit/Core/Report/ReportTest.php +++ b/extension/Tests/Unit/Core/Report/ReportTest.php @@ -1384,6 +1384,9 @@ EOF; */ public function testReportPageWrapper() { + // Report notation 'alias' with alias + $this->store->setVar(TOKEN_ALIAS . '.1', 'myAlias', STORE_TYPO3); + $line = <<<EOF 10.sql = SELECT firstname FROM Person ORDER BY id LIMIT 2 10.head = <table> @@ -1403,6 +1406,32 @@ EOF; 10.10.tail = Dynamic tail 10.10.althead = No record found 10.10.altsql = SELECT 'alt sql fired' +EOF; + + $result = $this->report->process($line); + $expect = "<table><tr><td>John</td><br>Static headDynamic headnestedDynamic tailStatic tail</tr>--<tr><td>Jane</td><br>Static headNo record foundalt sql firedStatic tail</tr></table>"; + $this->assertEquals($expect, $result); + + // Report notation 'alias' with alias + $line = <<<EOF +1.sql = SELECT firstname FROM Person ORDER BY id LIMIT 2 +1.head = <table> +1.tail = </table> +1.rbeg = <tr> +1.rend = <br> +1.renr = </tr> +1.fbeg = <td> +1.fend = </td> +1.rsep = -- +1.fsep = ++ + +1.2.sql = SELECT 'nested' FROM (SELECT '') AS fake WHERE '{{myAlias.line.count}}'='1' +1.2.shead = Static head +1.2.stail = Static tail +1.2.head = Dynamic head +1.2.tail = Dynamic tail +1.2.althead = No record found +1.2.altsql = SELECT 'alt sql fired' EOF; $result = $this->report->process($line); @@ -1425,6 +1454,9 @@ EOF; */ public function testReportContent() { + // Report notation 'alias' with alias + $this->store->setVar(TOKEN_ALIAS . '.1', 'myAlias', STORE_TYPO3); + $line = <<<EOF 10.sql = SELECT 'Hello' 20.sql = SELECT 'World' @@ -1434,6 +1466,16 @@ EOF; $expect = "HelloWorld{{10.line.content}}"; $this->assertEquals($expect, $result); + // Report notation 'alias' with alias + $line = <<<EOF +1.sql = SELECT 'Hello' +2.sql = SELECT 'World' +2.tail = {{myAlias.line.content}} +EOF; + $result = $this->report->process($line); + $expect = "HelloWorld{{myAlias.line.content}}"; + $this->assertEquals($expect, $result); + $line = <<<EOF 10.sql = SELECT 'Hello' 10.content = hide @@ -1444,6 +1486,17 @@ EOF; $expect = "WorldHello"; $this->assertEquals($expect, $result); + // Report notation 'alias' with alias + $line = <<<EOF +1.sql = SELECT 'Hello' +1.content = hide +2.sql = SELECT 'World' +2.tail = {{myAlias.line.content}} +EOF; + $result = $this->report->process($line); + $expect = "WorldHello"; + $this->assertEquals($expect, $result); + $line = <<<EOF 10.sql = SELECT 'Hello' 10.content = show @@ -1454,6 +1507,17 @@ EOF; $expect = "HelloWorldHello"; $this->assertEquals($expect, $result); + // Report notation 'alias' with alias + $line = <<<EOF +1.sql = SELECT 'Hello' +1.content = show +2.sql = SELECT 'World' +2.tail = {{myAlias.line.content}} +EOF; + $result = $this->report->process($line); + $expect = "HelloWorldHello"; + $this->assertEquals($expect, $result); + // Check that current row can be reused in head, tail, rbeg, rend, renr $line = <<<EOF 10.sql = SELECT 'Hello' @@ -1468,11 +1532,37 @@ EOF; $expect = "HelloHelloHelloHelloHelloHello"; $this->assertEquals($expect, $result); + // Report notation 'alias' with alias + // Check that current row can be reused in head, tail, rbeg, rend, renr + $line = <<<EOF +1.sql = SELECT 'Hello' +1.content = show +1.head = {{myAlias.line.content}} +1.tail = {{myAlias.line.content}} +1.rbeg = {{myAlias.line.content}} +1.rend = {{myAlias.line.content}} +1.renr = {{myAlias.line.content}} +EOF; + $result = $this->report->process($line); + $expect = "HelloHelloHelloHelloHelloHello"; + $this->assertEquals($expect, $result); + // Check single tick escape $line = <<<EOF 10.sql = SELECT "Hel'lo" 10.content = hide 20.sql = SELECT '--{{10.line.content::s}}--', "--{{10.line.content}}--", "--{{10.line.content::s}}--" +EOF; + $result = $this->report->process($line); + $expect = "--Hel'lo----Hel'lo----Hel'lo--"; + $this->assertEquals($expect, $result); + + // Report notation 'alias' with alias + // Check single tick escape + $line = <<<EOF +1.sql = SELECT "Hel'lo" +1.content = hide +2.sql = SELECT '--{{myAlias.line.content::s}}--', "--{{myAlias.line.content}}--", "--{{myAlias.line.content::s}}--" EOF; $result = $this->report->process($line); $expect = "--Hel'lo----Hel'lo----Hel'lo--"; @@ -1563,15 +1653,27 @@ EOF; */ public function testReportVariables() { + // Report notation 'alias' with alias + $this->store->setVar(TOKEN_ALIAS . '.1', 'myAlias', STORE_TYPO3); + $this->store->setVar(TOKEN_ALIAS . '.1.2', 'myAlias2', STORE_TYPO3); + $result = $this->report->process("10.sql = SELECT 'normal ', 'hidden' AS _hidden, 'text ' FROM Person ORDER BY id LIMIT 1"); $this->assertEquals("normal text ", $result); $result = $this->report->process("10.sql = SELECT 'normal ', 'hidden' AS _hidden, 'text ' FROM Person ORDER BY id LIMIT 1\n10.10.sql = SELECT '{{10.hidden}}'"); $this->assertEquals("normal text hidden", $result); + // Report notation 'alias' with alias + $result = $this->report->process("1.sql = SELECT 'normal ', 'hidden' AS _hidden, 'text ' FROM Person ORDER BY id LIMIT 1\n1.2.sql = SELECT '{{myAlias.hidden}}'"); + $this->assertEquals("normal text hidden", $result); + $result = $this->report->process("10.sql = SELECT 'normal ', 'hidden' AS _hidden, 'text ' FROM Person ORDER BY id LIMIT 1\n10.10.sql = SELECT '{{10.unknown}}'"); $this->assertEquals("normal text {{10.unknown}}", $result); + // Report notation 'alias' with alias + $result = $this->report->process("1.sql = SELECT 'normal ', 'hidden' AS _hidden, 'text ' FROM Person ORDER BY id LIMIT 1\n1.2.sql = SELECT '{{myAlias.unknown}}'"); + $this->assertEquals("normal text {{myAlias.unknown}}", $result); + $result = $this->report->process("10.sql = SELECT 'normal ', 'hidden' AS _hidden, 'text ' FROM Person ORDER BY id LIMIT 1\n10.10.sql = SELECT '{{fake}}'"); $this->assertEquals("normal text {{fake}}", $result); @@ -1588,12 +1690,24 @@ EOF; $result = $this->report->process("10.sql = SELECT 'normal ', 'hidden' AS _hidden, 'text ' FROM Person ORDER BY id\n10.10.sql = SELECT '{{fake:V}}-{{10.line.count}}-{{10.line.total}}-{{10.line.insertId}} '"); $this->assertEquals("normal text hello world -1-2-0 normal text hello world -2-2-0 ", $result); + // Report notation 'alias' with alias + $result = $this->report->process("1.sql = SELECT 'normal ', 'hidden' AS _hidden, 'text ' FROM Person ORDER BY id\n1.2.sql = SELECT '{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}} '"); + $this->assertEquals("normal text hello world -1-2-0 normal text hello world -2-2-0 ", $result); + $result = $this->report->process("10.sql = SELECT 'normal ', 'hidden' AS _hidden, 'text ' FROM Person ORDER BY id\n10.10.sql = SELECT '{{fake:V}}-{{10.line.count}}-{{10.line.total}} '"); $this->assertEquals("normal text hello world -1-2 normal text hello world -2-2 ", $result); + // Report notation 'alias' with alias + $result = $this->report->process("1.sql = SELECT 'normal ', 'hidden' AS _hidden, 'text ' FROM Person ORDER BY id\n1.2.sql = SELECT '{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}} '"); + $this->assertEquals("normal text hello world -1-2 normal text hello world -2-2 ", $result); + $result = $this->report->process("10.sql = SELECT 'normal ', 'hidden' AS _hidden, 'text ' FROM Person ORDER BY id\n10.10.sql = SELECT '{{fake:V}}-{{10.line.count}}-{{10.line.total}}-{{10.10.line.count}}-{{10.10.line.total}} '"); $this->assertEquals("normal text hello world -1-2-1-1 normal text hello world -2-2-1-1 ", $result); + // Report notation 'alias' with alias + $result = $this->report->process("1.sql = SELECT 'normal ', 'hidden' AS _hidden, 'text ' FROM Person ORDER BY id\n1.2.sql = SELECT '{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias2.line.count}}-{{myAlias2.line.total}} '"); + $this->assertEquals("normal text hello world -1-2-1-1 normal text hello world -2-2-1-1 ", $result); + $result = $this->report->process("10.sql = SELECT 'normal ', 'hidden' AS _hidden, 'text ' FROM Person ORDER BY id\n10.10.sql = SELECT '{{fake:V:::not found}} '"); $this->assertEquals("normal text hello world normal text hello world ", $result); @@ -1643,6 +1757,26 @@ EOF; $expect = "h:hello world-1-2-0,rb:hello world-1-2-0,-fb:hello world-1-2-0,Doe-fe:hello world-1-2-0,fs:hello world-1-2-0,-fb:hello world-1-2-0,John-fe:hello world-1-2-0,re:hello world-1-2-0,rr:hello world-1-2-0,rs:hello world-1-2-0,rb:hello world-2-2-0,-fb:hello world-2-2-0,Smith-fe:hello world-2-2-0,fs:hello world-2-2-0,-fb:hello world-2-2-0,Jane-fe:hello world-2-2-0,re:hello world-2-2-0,rr:hello world-2-2-0,t:hello world-2-2-0,"; $this->assertEquals($expect, $result); + // Report notation 'alias' with alias + $line = <<<EOF +1.sql = SELECT name, firstname FROM Person ORDER BY id LIMIT 2 +1.head = h:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.tail = t:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.rbeg = rb:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.rend = re:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.renr = rr:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.fbeg = -fb:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.fend = -fe:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.rsep = rs:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.fsep = fs:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +EOF; + + $this->store->setVar('fake', 'hello world', STORE_VAR); + $this->store->setVar(TOKEN_ALIAS . '.1', 'myAlias', STORE_TYPO3); + $result = $this->report->process($line); + $expect = "h:hello world-1-2-0,rb:hello world-1-2-0,-fb:hello world-1-2-0,Doe-fe:hello world-1-2-0,fs:hello world-1-2-0,-fb:hello world-1-2-0,John-fe:hello world-1-2-0,re:hello world-1-2-0,rr:hello world-1-2-0,rs:hello world-1-2-0,rb:hello world-2-2-0,-fb:hello world-2-2-0,Smith-fe:hello world-2-2-0,fs:hello world-2-2-0,-fb:hello world-2-2-0,Jane-fe:hello world-2-2-0,re:hello world-2-2-0,rr:hello world-2-2-0,t:hello world-2-2-0,"; + $this->assertEquals($expect, $result); + $line = <<<EOF 10.sql = SELECT name, firstname FROM Person ORDER BY id LIMIT 2 10.10.sql = SELECT ' blue ' @@ -1662,6 +1796,26 @@ EOF; $expect = "DoeJohnh:hello world-1-2-0,rb:hello world-1-2-0,-fb:hello world-1-2-0, blue -fe:hello world-1-2-0,re:hello world-1-2-0,rr:hello world-1-2-0,t:hello world-1-2-0,SmithJaneh:hello world-2-2-0,rb:hello world-2-2-0,-fb:hello world-2-2-0, blue -fe:hello world-2-2-0,re:hello world-2-2-0,rr:hello world-2-2-0,t:hello world-2-2-0,"; $this->assertEquals($expect, $result); + // Report notation 'alias' with alias + $line = <<<EOF +1.sql = SELECT name, firstname FROM Person ORDER BY id LIMIT 2 +1.2.sql = SELECT ' blue ' +1.2.head = h:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.2.tail = t:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.2.rbeg = rb:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.2.rend = re:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.2.renr = rr:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.2.fbeg = -fb:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.2.fend = -fe:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.2.rsep = rs:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +1.2.fsep = fs:{{fake:V}}-{{myAlias.line.count}}-{{myAlias.line.total}}-{{myAlias.line.insertId}}, +EOF; + + $this->store->setVar('fake', 'hello world', STORE_VAR); + $this->store->setVar(TOKEN_ALIAS . '.1', 'myAlias', STORE_TYPO3); + $result = $this->report->process($line); + $expect = "DoeJohnh:hello world-1-2-0,rb:hello world-1-2-0,-fb:hello world-1-2-0, blue -fe:hello world-1-2-0,re:hello world-1-2-0,rr:hello world-1-2-0,t:hello world-1-2-0,SmithJaneh:hello world-2-2-0,rb:hello world-2-2-0,-fb:hello world-2-2-0, blue -fe:hello world-2-2-0,re:hello world-2-2-0,rr:hello world-2-2-0,t:hello world-2-2-0,"; + $this->assertEquals($expect, $result); }