{"id":24540687,"url":"https://github.com/mistralys/mailcode","last_synced_at":"2025-06-13T17:36:05.541Z","repository":{"id":57017631,"uuid":"240225584","full_name":"Mistralys/mailcode","owner":"Mistralys","description":"Mailcode syntax parsing library for PHP","archived":false,"fork":false,"pushed_at":"2025-04-11T08:21:18.000Z","size":1525,"stargazers_count":0,"open_issues_count":7,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-11T10:35:29.310Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Mistralys.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-02-13T09:45:11.000Z","updated_at":"2025-04-11T08:18:57.000Z","dependencies_parsed_at":"2023-11-17T08:53:14.565Z","dependency_job_id":"9a4e38ce-3584-4c85-904b-c0de2cc8b962","html_url":"https://github.com/Mistralys/mailcode","commit_stats":{"total_commits":798,"total_committers":1,"mean_commits":798.0,"dds":0.0,"last_synced_commit":"e7c89ce76657adfc401e5a726c04b9106b939ee3"},"previous_names":[],"tags_count":78,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mistralys%2Fmailcode","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mistralys%2Fmailcode/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mistralys%2Fmailcode/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mistralys%2Fmailcode/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Mistralys","download_url":"https://codeload.github.com/Mistralys/mailcode/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249038887,"owners_count":21202803,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-22T18:14:41.824Z","updated_at":"2025-04-15T08:42:34.297Z","avatar_url":"https://github.com/Mistralys.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Mistralys/mailcode/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/Mistralys/mailcode/?branch=master)\n\n# PHP Mailcode Syntax parser\n\nMailcode is a preprocessor command syntax created for use in emailings.\n\nIt aims to be easy to use by authors, and usable in a number of popular web formats, \nfrom plain text to HTML and XML. The Mailcode syntax is verbose by design, without \nshorthand notations, for both better readability and performance. \n\nIt has been developed to support interchangeable backend preprocessor syntaxes,\nto unify these into a single language. \n\n## The syntax\n\n### Base Structure\n\nAll commands follow the same structure.\n\nParameterless:\n\n```\n{command}\n```\n\nWith parameters:\n\n```\n{command subtype: parameters}\n```\n\nThe subtype can switch between modes of the same command.\n\nToggleable flags to enable/disable features:\n\n```\n{command subtype: parameters flagFoo: flagBar:}\n```\n\nNamed parameters:\n\n```\n{command subtype: \"value\" name=\"param value\"}\n```\n\n### Escaping special characters\n\n#### Double quotes\n\nString literals are expected to be quoted using double quotes (`\"`). \nTo use double quotes within a string literal, it can be escaped using \na backslash (`\\`):\n\n```\n{if contains: $PRODUCT.NAME \"Search term with \\\"quotes\\\"\"}\n```\n\n\u003e Note: When using the Factory to create commands, this is done automatically.\n\n#### Curly braces\n\nTo use curly braces in a document, or in string literals, they can be escaped:\n\n```\n{if contains: $PRODUCT.NAME \"With \\{braces\\}\"}\n   Literal braces: \\{end\\}\n{end}\n```\n\n## Supported commands\n\n### Display variable values\n\n```\n{showvar: $CUSTOMER.NAME}\n```\n\n### Display a date and time\n\nUsing the default date and time settings for the current locale:\n\n```\n{showdate: $ORDER.DATE}\n```\n\nWith a custom date/time format:\n\n```\n{showdate: $ORDER.DATE \"d/m/Y\"}\n```\n\n\u003e Note: Also see the [section on date formats](#date-formats) for details \n\u003e on how to specify date and time.\n\nIf no variable is specified, it is assumed that the current date \nand time should be used:\n\n```\n{showdate: \"d/m/Y\"}\n```\n\nWith a specific time zone:\n\n```\n{showdate: $ORDER.DATE \"d.m.Y\" timezone=\"Europe/Paris\"}\n{showdate: $ORDER.DATE \"d.m.Y\" timezone=$TIME_ZONE}\n```\n\nIf no time zone is specified, the default PHP time zone is used\n(this is typically `UTC` unless the server is configured differently). \n\nIt is possible to set the default time zone globally for the command, \nseparately from the native PHP time zone:\n\n```php\nuse Mailcode\\Mailcode_Commands_Command_ShowDate;\n\nMailcode_Commands_Command_ShowDate::setDefaultTimezone('Europe/Paris');\n```\n\nThis will make all `showdate` commands use `Europe/Paris`, unless a\nspecific time zone is specified explicitly in a command.\n\n### Display a formatted number\n\nTo specify the format for the number, write the number `1000` the way you \nwould like to have it formatted. This will be applied to the values accordingly.\n\n```\n{shownumber: $ORDER.PRICE \"1,000.00\"}\n```\n\nThis will use commas as a thousand separator, a dot for the decimals, and \ntwo decimal positions.\n\nFor example, `10` will be displayed as `10.00`, and `5120.4` as `5,120.40`.\n\n#### Zero-Padding\n\nZero-padding is specified by appending the required number length like this:\n\n```\n{shownumber: $MONTH \"1000:##\"}\n```\n\nThe number of hashes determines the target length of the number. This example \nwill add a zero-padding of `2`, meaning a `5` will be shown as `05`.\n\n#### Absolute numbers\n\nWhen working with negative numbers, you can use the `absolute:` \nkeyword to ensure that the minus sign is not shown.\n\n```\n{shownumber: $ORDER.PRICE \"1,000.00\" absolute:}\n```\n\n### Display a price\n\nA number can be formatted to be displayed as a price with localized\nformatting and currency symbol/name.\n \nWith default settings (USD):\n\n```\n{showprice: $ORDER.PRICE}\n```\n\nWith a specific currency, as string or variable:\n\n```\n{showprice: $ORDER.PRICE currency=\"EUR\"}\n{showprice: $ORDER.PRICE currency=$ORDER.CURRENCY_ISO}\n```\n\nFor a specific region, as string or variable:\n\n```\n{showprice: $ORDER.PRICE region=\"fr_FR\"}\n{showprice: $ORDER.PRICE region=$ORDER.REGION}\n```\n\nAs an absolute number:\n\n```\n{showprice: $ORDER_PRICE absolute:}\n```\n\nUse the currency name (e.g. \"EUR\") instead of the symbol:\n\n```\n{showprice: $ORDER.PRICE currency-name:}\n```\n\n### Display a text snippet\n\nDisplay a raw text snippet. Newlines are converted to HTML `\u003cbr\u003e` \ntags automatically.\n\n```\n{showsnippet: $snippet_name}\n```\n\nTo disable the  `\u003cbr\u003e` tags, use the `nohtml:` keyword:\n\n```\n{showsnippet: $snippet_name nohtml:}\n```\n\nA namespace can be specified if the target snippet should not\nbe loaded from the global namespace:\n\n```\n{showsnippet: $snippet_name namespace=\"NamespaceName\"}\n```\n\n### Display a URL with or without tracking\n\nURLs may contain variables, or even logic commands. The `showurl` command\nmakes it possible to integrate these into tracking URLs, by rendering the\nfinal URL on the target language level.\n\n#### Adding tracking\n\nConsider the following URL:\n\n```\n{if variable: $COUNTRY == \"fr\"}\nhttps://mistralys.fr\n{else}\nhttps://mistralys.eu\n{end}\n```\n\nTo make this trackable, use the following command:\n\n```\n{showurl: \"TrackingID\"}\n{if variable: $COUNTRY == \"fr\"}\nhttps://mistralys.fr\n{else}\nhttps://mistralys.eu\n{end}\n{showurl}\n```\n\n\u003e NOTE: The command must be closed with {showurl}, not {end}.\n\nOn the target language level (e.g. Apache Velocity), this will evaluate \nthe result of the `if` command first, to resolve the final URL. This can \nthen be easily used in any tracking implementation, which also needs to\nbe implemented on the target language level.\n\n#### Tracking IDs\n\nThe tracking ID is used to identify the location of the link in the \ndocument, e.g. `header-image`. If it is omitted or empty, an automatic\nID will be generated.\n\nThe minimum version of the command looks like this:\n\n```\n{showurl: \"\"}\nhttps://mistralys.eu\n{showurl}\n```\n\nThe default generated tracking ID follows this scheme: `link-001`, with\na link counter that is unique for the whole request. A custom ID generator\ncan be registered like this:\n\n```php\nuse \\Mailcode\\Mailcode_Commands_Command_ShowURL;\nuse \\Mailcode\\Commands\\Command\\ShowURL\\AutoTrackingID;\n\n// The method expects a callable, which must return a string.\nAutoTrackingID::setGenerator(static function(Mailcode_Commands_Command_ShowURL $command) : string \n{\n    return 'trackingID';\n});\n```\n\n#### Adding query parameters\n\nThe command allows specifying additional query parameters that should\nbe added to the target URL, like UTM parameters or the like.\n\nExample command:\n\n```\n{showurl: \"TrackingID\" \"foo=bar\" \"other=value\"}\nhttps://mistralys.eu\n{showurl}\n```\n\nResulting example tracking URL:\n\n```\nhttps://track.domain?id=TrackingID\u0026target=https%3A%2F%2Fmistralys.eu%3Ffoo%3Dbar%26other%3Dvalue\n```\n\n#### Disabling the tracking\n\nThe tracking can be disabled with the `no-tracking:` keyword,\nin which case only the evaluated URL is used. Additional query\nparameters can still be added.\n\n```\n{showurl: no-tracking: \"foo=bar\"}\nhttps://mistralys.eu\n{showurl}\n```\n\nResulting URL:\n\n```\nhttps://mistralys.eu?foo=bar\n```\n\n### Phone numbers in URLs\n\nThe `{showphone}` command can convert a phone number in a country-specific or international\nformatted style to the E164 format required for `tel:` URLs.\n\nWhenever you wish to add a phone link, use this:\n\n```\n\u003ca href=\"tel:{showphone: $PHONE \"US\" urlencode:}\"\u003e{showvar: $PHONE}\u003c/a\u003e\n```\n\nThis will convert the phone number to the expected format.\n\n### Set a variable\n\n#### String value\n\n```\n{setvar: $CUSTOMER.NAME = \"value\"}\n```\n\n#### Arithmetic operation\n\nBasic arithmetic operations can be used, provided the target\nlanguage supports these. They are typically passed on directly \nthrough the translator, unless it has special logic to convert\nthem.\n\n```\n{setvar: $AMOUNT = 45 * 2}\n```\n\n#### Counting lists \n\nThe `count` parameter allows specifying a list variable\nto count the records of, and store the amount in the\ntarget variable.\n\n```\n{setvar: $AMOUNT count=$LIST_VAR}\n```\n\n#### Omitting the = sign\n\nThe equal sign is implied, so it can be omitted.\n\n```\n{setvar: $AMOUNT 45 * 2}\n```\n\n### IF conditionals\n\n#### Variable-based conditions\n\n```\n{if variable: $CUSTOMER.NAME == \"John\"}\n    Hi, John.\n{elseif variable: $CUSTOMER.NAME == \"Jack\"}\n    Howdy, Jack.\n{end}\n```\n\n#### Checking for empty or non-empty variables\n\nChecking if a variable does not exist, or is empty:\n\n```\n{if empty: $CUSTOMER.NAME}\n    Customer name is empty.\n{end}\n```\n\nChecking if a variable exists and is not empty:\n\n```\n{if not-empty: $CUSTOMER.NAME}\n    {showvar: $CUSTOMER.NAME}\n{end}\n```\n\n#### Searching for substrings\n\nChecking if a variable value contains a string:\n\n```\n{if contains: $PRODUCT.NAME \"Search term\"}\n```\n\nCase-insensitive search:\n\n```\n{if contains: $PRODUCT.NAME \"Search term\" insensitive:}\n```\n\nSearching for multiple terms (applied if any of the terms is found):\n\n```\n{if contains: $PRODUCT.NAME \"Term 1\" \"Term 2\" \"Term 3\"}\n```\n\nMatching a variable value if it does NOT contain any of the search terms:\n\n```\n{if not-contains: $PRODUCT.NAME \"Term 1\" \"Term 2\" \"Term 3\"}\n```\n\n#### Searching lists by partial matches\n\nIf a variable contains several records, it is possible to search through a property \nin all records, without having to use a loop:\n\n```\n{if list-contains: $PRODUCTS.NAME \"Server\"}\n```\n\nThis will search in the `NAME` property of all products for the specified search term.\n\nThe command otherwise behaves like  the `contains` command, with the same options.\n\nCase-insensitive search:\n\n```\n{if list-contains: $PRODUCTS.NAME \"server\" insensitive:}\n```\n\nNegating the search, applying it only if the search terms are not found:\n\n```\n{if list-not-contains: $PRODUCTS.NAME \"Hosting\" \"WordPress\"}\n```\n\n#### Searching lists by regular expressions\n\nThe `list-contains` can be switched to regex mode with the `regex:` keyword:\n\n```\n{if list-contains: $PRODUCTS.NAME regex: \"\\\\ASuperName\\\\Z\"}\n```\n\n  \u003e NOTE: This can be combined with the `insensitive:` keyword to make the\n    regular expression case-insensitive.\n\nRegular expressions may use curly braces when defining quantifiers, e.g. `{1,5}`.\nThis is a special case where you do not have to escape the braces. The parser\nwill recognize these braces so the regex stays readable. \n\nThese commands are both valid:\n\n```\n{if list-contains: $PRODUCTS.NAME regex: \"[0-9]{1,3}\"}\n\n{if list-contains: $PRODUCTS.NAME regex: \"[0-9]\\{1,3\\}\"}\n```\n\n#### Searching lists by exact matches\n\nUsing regular expressions allows searching for exact matches by using the\nbeginning and end anchors `\\A` and `\\Z`, but this is unwieldy and not\nexactly readable. The `list-equals` command does exactly this.\n\n```\n{if list-equals: $PRODUCT.NAME \"Search\"}\n```\n\nThis will match only if an entry in the list is an exact match for \"Search\".\n\nIt can be combined with the `insensitive:` keyword to search for the full\nsearch term, but in a case-insensitive way.\n\n#### Searching lists by beginning or end\n\nChecking if a variable value starts with a specific string:\n\n```\n{if begins-with: $PRODUCT.NAME \"Search\"}\n```\n\nOr checking if it ends with a specific string:\n\n```\n{if ends-with: $PRODUCT.NAME \"term\"}\n```\n\nBoth can be made case-insensitive:\n\n```\n{if begins-with: $PRODUCT.NAME \"Search\" insensitive:}\n```\n\n#### Numeric comparisons\n\nChecking if a variable value is bigger than a specific number:\n\n```\n{if bigger-than: $PRODUCT.PRICE \"220\"}\n```\n\nOr checking if it is smaller:\n\n```\n{if smaller-than: $PRODUCT.PRICE \"220\"}\n```\n\nOr checking for an exact match:\n\n```\n{if equals-number: $PRODUCT.PRICE \"220\"}\n``` \n\n#### Freeform conditions:\n\nWithout subtype, the `IF` condition is not validated, and will be passed through as-is to the translation backend.\n\n```\n{if: 6 + 2 == 8}\n    It means 8.\n{end}\n```\n\n#### `AND` and `OR` combinations\n\nSeveral conditions can be combined within the same command using the `and:` and `or:` keywords. Either can be used, but not both within the same command. Subtypes can be mixed at will.\n\nUsing AND:\n\n```\n{if variable: $ORDER.MONTH == 8 and contains: $ORDER.TYPE \"new_customer\"}\n    New customer order in August.\n{end}\n```\n\nUsing OR:\n\n```\n{if not-empty: $CUSTOMER.POSTCODE or variable: $CUSTOMER.USE_INVOICE == \"true\"}\n```\n\n### Loops\n\n```\n{for: $NAME in $CUSTOMER.NAMES}\n    {showvar: $NAME}\n{end}\n```\n\n#### Breaking out of loops\n\n```\n{for: $NAME in $CUSTOMER.NAMES}\n    {if variable: $NAME == \"John Doe\"}\n        {break}\n    {end}\n    {showvar: $NAME}\n{end}\n```\n\n#### Stopping at a specific iteration\n\nThe `break-at` parameter allows stopping the loop at a \nspecific loop iteration count (`0`-based).\n\n```\n{for: $ENTRY in $CUSTOMERS break-at=6}\n    {showvar: $ENTRY.NAME}\n{end}\n```\n\n### Comments\n\nComments may be added to document things. Whether they are used when translated\nto a specific preprocessor language depends on the translator. In general, \nthe comments are converted to the target language.\n\n```\n{comment: \"This is a comment.\"}\n```\n\n  \u003e NOTE: Comments can contain special characters, except other Mailcode commands\n    or texts that can be mistaken for commands (which use the brackets {}).\n\n## Encoding and decoding values\n\n### URL encoding\n\nTo URL encode a variable value:\n\n```\n{showvar: $CUSTOMER.NAME urlencode:}\n```\n\nThe opposite is also possible:\n\n```\n{showvar: $CUSTOMER.NAME urldecode:}\n```\n\n### IDN encoding\n\nTo encode a variable value to IDN:\n\n```\n{showvar: $CUSTOMER.NAME idnencode:}\n```\n\nThe opposite is also possible:\n\n```\n{showvar: $CUSTOMER.NAME idndecode:}\n```\n\n### Encrypted values\n\nEncrypted values can be decrypted at render time in the target backend\nsystem. This is intended to be used with key names, which uniquely \nidentify the relevant encryption key to use to decrypt the values on \nthe backend side.\n\nTo use the backend system's default encryption key name, add the parameter \nwith an empty value:\n\n```\n{showvar: $CUSTOMER.NAME decrypt=\"\"}\n```\n\nTo use a specific key name:\n\n```\n{showvar: $CUSTOMER.NAME decrypt=\"keyname\"}\n```\n\n#### Default key names\n\nIt is also possible to set a default key name that will be automatically\nused for all commands with an empty decrypt parameter:\n\n```php\nuse Mailcode\\Decrypt\\DecryptSettings;\n\nDecryptSettings::getDefaultKeyName('default-key');\n```\n\nAfter this method is called, the following commands are functionally equivalent:\n\n```\n{showvar: $CUSTOMER.NAME decrypt=\"\"}\n{showvar: $CUSTOMER.NAME decrypt=\"default-key\"}\n```\n\n\n## Integrated preprocessing\n\nMailcode is a preprocessor language meant to be interpreted by a preprocessor\nservice, but some commands are made to be preprocessed natively by Mailcode \nitself. One example is the `mono` command, which applies monospace formatting\nto text.\n\nThe preprocessing is optional and can be done with the specialized PreProcessor\nclass.\n\n  \u003e NOTE: When translating to an output syntax like Apache Velocity, the default\n    behavior is to strip out leftover preprocessor commands, so there can be no \n    Mailcode commands in the translated text.\n\n### Working with the PreProcessor\n\nThe PreProcessor is very easy to use: feed it a string with Mailcode \ncommands, and all commands that support pre-processing will be rendered.\nAfter this, the resulting string can be passed into a safeguard instance or\nparsed to fetch the commands.\n\n```php\n$subject = '(Mailcode text)';\n\n$processor = \\Mailcode\\Mailcode::create()-\u003ecreatePreProcessor($subject);\n$result = $processor-\u003erender();\n```\n\n  \u003e NOTE: While the preprocessing can be done after safeguarding a text,\n    it is recommended to do it beforehand, to avoid the overhead of\n    unnecessarily parsing the commands. Also, these commands may actually \n    generate new Mailcode syntax to parse.\n\n### Format a text as code\n\n```\nThis text is {mono}monospaced{end}.\n```\n\nThe resulting pre-processed text will look like this:\n\n```html\nThis text is \u003ccode\u003emonospaced\u003c/code\u003e.\n```\n\nTo create a `\u003cpre\u003e` tag, add the multiline keyword:\n\n```\n{mono: multiline:}\nThis is a multiline code block.\n{end}\n```\n\nThis gives the following pre-processed text:\n\n```html\n\u003cpre\u003e\nThis is a multiline code block.\n\u003c/pre\u003e\n```\n\n## Working with commands\n\n### Closing, opening and sibling commands\n\nCommands like for loops, and if statements that have a closing command\nand are closed using the `{end}` command support accessing their siblings,\nand respective opening and closing commands.\n\nFor example, the closing command of an `IF` statement has the `getOpeningCommand()`\nmethod, which returns the `IF` command that it closes, and vice versa. If command\nstructures with `elseif` and `else` commands allow traversing the whole list of\nsibling commands.\n\nThis makes it easy to work with complex command structures.\n\n## Date formats\n\n### Supported formatting characters\n\nThe ShowDate command uses formatting characters that are compatible with PHP's \ndate formatting functions, but only a subset of these are allowed.\n\n  * `d` Day number, with leading zeros\n  * `j` Day number, without leading zeros\n  * `m` Month number, with leading zeros\n  * `n` Month number, without leading zeros\n  * `y` Year, with two digits\n  * `Y` Year, with four digits\n  * `H` Hour, 24-hour format, with leading zeros\n  * `G` Hour, 24-hour format, without leading zeros\n  * `h` Hour, 12-hour format, with leading zeros\n  * `g` Hour, 12-hour format, without leading zeros\n  * `a` AM/PM marker, lowercase\n  * `i` Minutes, with leading zeros\n  * `s` Seconds, with leading zeros \n  * `v` Milliseconds\n  * `e` Time zone identifier, e.g. \"CET\", \"Europe/Paris\"\n  \nAdditionally, the following punctuation characters may be used:\n\n  * `.` Dot\n  * `/` Slash\n  * `-` Hyphen\n  * `:` Colon\n  * ` ` Space     \n\n### Accessing format information\n\nThe Mailcode_Date_FormatInfo class can be used to access information on the \navailable date formats when using the ShowDate command. It is available \nglobally via a factory method:\n\n```php\nuse Mailcode\\Mailcode_Factory;\n\n$dateInfo = Mailcode_Factory::createDateInfo();\n```\n\n### Setting defaults\n\nThe ShowDate command uses `Y/m/d` as default date format. The format info class \ncan be used to overwrite this:\n\n```php\nuse Mailcode\\Mailcode_Factory;\n\n$dateInfo = Mailcode_Factory::createDateInfo();\n$dateInfo-\u003esetDefaultFormat('d.m.Y');\n```\n\nOnce it has been set, whenever the ShowDate command is used without specifying\na custom format string, it will use this default format.  \n\n### Accessing formatting characters programmatically\n\nTo make it possible to integrate mailcode in existing documentation, the format \ninfo class offers the `getFormatCharacters()` method to get a list of all \ncharacters that can be used. \n\nDisplaying a simple text-based list of allowed characters:\n\n```php\nuse Mailcode\\Mailcode_Factory;\n\n$dateInfo = Mailcode_Factory::createDateInfo();\n$characters = $dateInfo-\u003egetCharactersList();\n\nforeach($characters as $character)\n{\n    echo sprintf(\n        '%s: \"%s\" %s',\n        $character-\u003egetTypeLabel(),\n        $character-\u003egetChar(),\n        $character-\u003egetDescription()\n    );\n    \n    echo PHP_EOL;\n}\n```\n\n### Manually validating a date format\n\nUse the `validateFormat()` method to validate a date format string, and \nretrieve a validation message manually. The same method is used by the \n`ShowDate` command, but can be used separately for specific needs.\n\n```php\nuse Mailcode\\Mailcode_Factory;\n\n$formatString = \"d.m.Y H:i\";\n\n$dateInfo = Mailcode_Factory::createDateInfo();\n$result = $dateInfo-\u003evalidateFormat($formatString);\n\nif($result-\u003eisValid())\n{\n    echo 'Format is valid.';\n}\nelse\n{\n    echo sprintf(\n        'Format is invalid: Error #%s, %s',\n        $result-\u003egetCode(),\n        $result-\u003egetErrorMessage()\n    );\n}\n```\n\n## Format compatibility\n\nMailcode mixes well with HTML and XML. Its strict syntax makes it easy to \ndistinguish it from most text formats. with the notable exception of CSS. \nIn HTML, all style tags are ignored.\n\n## Safeguarding commands when filtering texts\n\nWhen texts containing commands need to be filtered, or otherwise parsed in a \nway that could break the command syntax, the safeguard mechanism allows for \neasy replacement of all commands with neutral placeholder strings.\n\nAssuming the text to filter, possibly containing commands, is stored in \n`$text`:\n\n```php\nuse Mailcode\\Mailcode;\n\n$text = '(commands here)';\n\n// create the safeguard instance for the text\n$safeguard = Mailcode::create()-\u003ecreateSafeguard($text);\n\nif(!$safeguard-\u003eisValid()) \n{\n    // there are invalid commands in the text\n}\n\n// replace all commands with placeholders\n$filterText = $safeguard-\u003emakeSafe();\n\n// do any required filtering and processing of the text\n\n// restore the placeholders to the full command texts\n$result = $safeguard-\u003emakeWhole($filterText);\n```\n\n**HINT:** Placeholders are case neutral, and thus cannot be broken by \nchanging the text case.  \n\n### Avoiding delimiter conflicts\n\nBy default, the placeholders use `999` as delimiters, for example:\n`9990000000001999`. Each delimiter gets a unique number within the same \nrequest, which is zero-padded right, making each placeholder unique in all \nsubject strings.\n\nHaving number-based placeholders means that they are impervious to usual text \ntransformations, like changing the case or applying url encoding.\n\nStill, the delimiter string can be adjusted as needed:\n\n```php\nuse \\Mailcode\\Mailcode;\n\n$text = '(Text with mailcode commands)';\n$safeguard = Mailcode::create()-\u003ecreateSafeguard($text);\n\n$safeguard-\u003esetDelimiter('__');\n```\n\nThis would for example make the delimiters look like `__0000000001__`.\n\n### Placeholder consistency check\n\nWhen calling `makeWhole()`, the Safeguard will make sure that all placeholders \ninitially replaced in the target string are still there. If they are not, an \nexception will be thrown.\n\n### Accessing placeholder information\n\nThe placeholders used in a string can be easily retrieved. Be sure to call \n`getPlaceholders()` after the initial configuration (setting the delimiters, for \nexample).\n\n```php\nuse \\Mailcode\\Mailcode;\n\n$text = '(Mailcode commands here)';\n$safeguard = Mailcode::create()-\u003ecreateSafeguard($text);\n\n$placeholders = $safeguard-\u003egetPlaceholdersCollection()-\u003egetAll();\n\nforeach($placeholders as $placeholder)\n{\n    $string = $placeholder-\u003egetReplacementText(); // the placeholder text\n    $command = $placeholder-\u003egetCommand(); // the detected command instance\n    $original = $placeholder-\u003egetOriginalText(); // the original command text\n}\n```\n\n## Applying formatting\n\nBy default, when using the safeguard's `makeWhole` method, all command \nplaceholders are replaced with the normalized syntax of the commands. \nA number of additional formatting options are available via the safeguard's \nformatting class. In this case, the formatted string is retrieved via the \nformatting class instead of the safeguard itself. \n\nCreating a formatting instance, using a safeguard:\n\n```php\nuse \\Mailcode\\Mailcode;\n\n$text = '(Mailcode commands here)';\n$safeguard = Mailcode::create()-\u003ecreateSafeguard($text);\n\n$formatting = $safeguard-\u003ecreateFormatting($safeguard-\u003emakeSafe());\n```\n\n**Note:** Formatting is entirely separate from the safeguard. The safeguard \ninstance retains the original text.\n\n### Replacers and Formatters\n\nThere are two types of formatters: \n\n  - **Replacers**: These will replace the command placeholders themselves (example: HTML syntax highlighting of commands). Only one replacer may be selected.\n  - **Formatters**: These will only modify the text around the placeholder, leaving the placeholder intact. Formatters can be combined at will.\n\nWhile it is not possible to select several replacers, they can be freely combined with formatters.\n\nThe methods to add formatters reflect their type:\n\n```php\nuse \\Mailcode\\Mailcode;\n\n$text = '(Mailcode commands here)';\n$safeguard = Mailcode::create()-\u003ecreateSafeguard($text);\n$formatting = $safeguard-\u003ecreateFormatting($safeguard-\u003emakeSafe());\n\n$formatting-\u003ereplaceWithHTMLHighlighting();\n$formatting-\u003eformatWithMarkedVariables();\n```\n\n### HTML Highlighting\n\nThe HTML syntax highlighter will add highlighting to all commands in an intelligent way. Commands will not be highlighted if they are used in HTML tag attributes or nested in tags where adding the highlighting markup would break the HTML structure.\n\n```php\nuse \\Mailcode\\Mailcode;\n\n$text = '(Mailcode commands here)';\n$safeguard = Mailcode::create()-\u003ecreateSafeguard($text);\n$formatting = $safeguard-\u003ecreateFormatting($safeguard-\u003emakeSafe());\n\n// Select to replace commands with syntax-highlighted commands\n$formatting-\u003ereplaceWithHTMLHighlighting();\n\n$highlighted = $formatting-\u003etoString();\n```\n\nThis will add the highlighting markup, but the necessary CSS styles must also be available in the document where the Mailcode will be displayed. More on this in the \"Loading the required styles\" section.\n\n#### Excluding tags from the highlighting\n\nBy default, commands will not be highlighted within the `\u003cstyle\u003e` and `\u003cscript\u003e` tags. Additional tags can easily be added to this list to customize it for your needs:\n\n```php\nuse \\Mailcode\\Mailcode;\n\n$text = '(Mailcode commands here)';\n$safeguard = Mailcode::create()-\u003ecreateSafeguard($text);\n$formatting = $safeguard-\u003ecreateFormatting($safeguard-\u003emakeSafe());\n\n// Get the formatter instance\n$formatter = $formatting-\u003ereplaceWithHTMLHighlighting();\n\n// add a single tag to the exclusion list\n$formatter-\u003eexcludeTag('footer');\n\n// add several tags at once\n$formatter-\u003eexcludeTags(array('footer', 'main', 'div'));\n```\n\nIn this example, commands nested in `\u003cfooter\u003e` tags will not be highlighted. \n\nNOTE: The excluded tag check goes up the whole tag nesting chain, which means that the following command would not be highlighted either, since it is contained in a tag that is nested within the `\u003cfooter\u003e` tag:\n\n```html\n\u003cfooter\u003e\n\t\u003cp\u003e\n\t\t\u003cb\u003e{showvar: $FOO}\u003c/b\u003e\n\t\u003c/p\u003e\n\u003c/footer\u003e\n```\n\n**WARNING:** The parser assumes that the HTML is valid. The tag nesting check does not handle nesting errors.\n\n#### Loading the required styles\n\nFor the highlighting to work, the according CSS styles need to be loaded in the target page. \n\nThere are two ways to do this:\n\n##### Including the stylesheet\n\nEnsure that the stylesheet file `css/highlight.css` of the package is loaded. \nThis requires knowing the exact URL to the package's vendor folder.\n\n```html\n\u003clink rel=\"stylesheet\" media=\"all\" src=\"/vendor/mistralys/mailcode/css/highlight.css\"\u003e\n```\n\n##### Using the Styler utility\n\nThe Styler utility class has a number of methods all around the CSS.\n\nCreating/getting the styler instance: \n\n```php\nuse Mailcode\\Mailcode;\n\n$styler = Mailcode::create()-\u003ecreateStyler();\n```\n\nGetting the raw CSS code without the `\u003cstyle\u003e` tag, for example, to use in a compiled stylesheet file:\n\n```php\nuse Mailcode\\Mailcode;\n\n$styler = Mailcode::create()-\u003ecreateStyler();\n\n$css = $styler-\u003egetCSS();\n```\n\nRetrieving the CSS including the `\u003cstyle\u003e` tag, for example, to add it inline in a page:\n\n```php\nuse Mailcode\\Mailcode;\n\n$styler = Mailcode::create()-\u003ecreateStyler();\n\n$styleTag = $styler-\u003egetStyleTag();\n```\n\nRetrieving the absolute path on disk to the stylesheet file:\n\n```php\nuse Mailcode\\Mailcode;\n\n$styler = Mailcode::create()-\u003ecreateStyler();\n\n$path = $styler-\u003egetStylesheetPath();\n```\n\nRetrieving the `\u003clink\u003e` tag programmatically, using the URL to access the `vendor` folder:\n\n```php\nuse Mailcode\\Mailcode;\n\n$styler = Mailcode::create()-\u003ecreateStyler();\n\n$linkTag = $styler-\u003egetStylesheetTag('/url/to/vendor/folder');\n```\n\nRetrieving the URL to the stylesheet file, using the URL to access the `vendor` folder:\n\n```php\nuse Mailcode\\Mailcode;\n\n$styler = Mailcode::create()-\u003ecreateStyler();\n\n$stylesheetURL = $styler-\u003egetStylesheetURL('/url/to/vendor/folder');\n```\n\n### Highlighting variables in the final document\n\nThe \"MarkVariables\" highlighter allows highlighting (not syntax highlighting) \nall variable type commands, even once they have been processed by the mail \npreprocessor. This is handy when testing to quickly identify all places in \nan HTML document where variables are used.\n\n```php\nuse Mailcode\\Mailcode;\n\n$htmlString = '(HTML with Mailcode commands here)';\n\n$safeguard = Mailcode::create()-\u003ecreateSafeguard($htmlString);\n$formatting = $safeguard-\u003ecreateFormatting($safeguard-\u003emakeSafe());\n\n// Get the formatter instance\n$formatter = $formatting-\u003eformatWithMarkedVariables();\n```\n\nLike the syntax highlighter, this will only highlight variables in valid contexts.\n\nNOTE: This can be combined with any of the other formatters, like the syntax highlighter.\n\n#### Load styles via style tag\n\nThe necessary style tag can be retrieved using the `getStyleTag` method:\n\n```php\nuse Mailcode\\Mailcode;\n\n$htmlString = '(HTML with Mailcode commands here)';\n\n$safeguard = Mailcode::create()-\u003ecreateSafeguard($htmlString);\n$formatting = $safeguard-\u003ecreateFormatting($safeguard-\u003emakeSafe());\n$formatter = $formatting-\u003eformatWithMarkedVariables();\n\n$styles = $formatter-\u003egetStyleTag();\n```\n\nThis then only has to be added to the target document.\n\n#### Integrate styles inline\n\nFor HTML mailings, or cases where the styles cannot be easily injected, the inline mode will automatically add the necessary styles to every command instance.\n\nEnable the inline mode like this:\n\n```php\nuse Mailcode\\Mailcode;\n\n$htmlString = '(HTML with Mailcode commands here)';\n\n$safeguard = Mailcode::create()-\u003ecreateSafeguard($htmlString);\n$formatting = $safeguard-\u003ecreateFormatting($safeguard-\u003emakeSafe());\n$formatter = $formatting-\u003eformatWithMarkedVariables();\n\n$formatter-\u003emakeInline();\n```\n\n\n## Translation to other syntaxes\n\nThe translator class makes it easy to convert documents with mailcode to other syntaxes, like the bundled Apache Velocity converter.\n\n### Translating whole strings\n\n```php\nuse Mailcode\\Mailcode;\n\n$string = '(Text with Mailcode commands here)';\n\n// create the safeguarder instance for the subject string\n$safeguard = Mailcode::create()-\u003ecreateSafeguard($string);\n\n// create the translator\n$apache = Mailcode::create()-\u003ecreateTranslator()-\u003ecreateApacheVelocity();\n\n// convert all commands in the safeguarded string\n$convertedString = $apache-\u003etranslateSafeguard($safeguard);\n```\n\n### Translating single commands\n\n```php\nuse Mailcode\\Mailcode;\nuse Mailcode\\Mailcode_Factory;\n\n// create the translator\n$apache = Mailcode::create()-\u003ecreateTranslator()-\u003ecreateApacheVelocity();\n\n// create a command\n$command = Mailcode_Factory::set()-\u003evar('VAR.NAME', '8');\n\n// convert it to an apache velocity command string\n$apacheString = $apache-\u003etranslateCommand($command);\n```\n\n### Translate to: Apache Velocity\n\nSee the [Velocity documentation][].\n\n### Translate to: Hubspot HubL\n\nSee the [HubL documentation][].\n\n## Browser-enabled tools\n\nIn the subfolder `tools` are a few utilities meant to be used in a browser. To use\nthese, run a `composer install` in the package's folder, and point your browser there.\n\n- Syntax translator: Translate a document with Mailcode commands to a supported syntax.\n- Syntax highlighter: Syntax highlighting of a document with Mailcode commands.\n- Phone countries extractor: Extracts a country list for the `showphone` command.\n\n\n[DateTool]: https://velocity.apache.org/tools/devel/apidocs/org/apache/velocity/tools/generic/DateTool.html\n[EscapeTool]: https://velocity.apache.org/tools/devel/apidocs/org/apache/velocity/tools/generic/EscapeTool.html\n[StringUtils]: http://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringUtils.html\n[LibPhoneNumber]: https://github.com/google/libphonenumber  \n[Velocity documentation]: https://github.com/Mistralys/mailcode/tree/master/docs/user-guide/translate-apache-velocity.md\n[HubL documentation]: https://github.com/Mistralys/mailcode/tree/master/docs/user-guide/translate-hubl.md\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmistralys%2Fmailcode","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmistralys%2Fmailcode","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmistralys%2Fmailcode/lists"}