{"id":20339051,"url":"https://github.com/szczyglis-dev/ultimate-chain-parser","last_synced_at":"2025-10-08T06:52:41.165Z","repository":{"id":62549267,"uuid":"484163637","full_name":"szczyglis-dev/ultimate-chain-parser","owner":"szczyglis-dev","description":"[PHP] Advanced, extendable, and configurable text data parsing and processing toolkit working in a chain-based flow. The concept of the application is based on processing in subsequent iterations using configurable data processing modules in a configured manner. Each element in the execution chain accesses the output of the previous element.","archived":false,"fork":false,"pushed_at":"2024-08-26T15:05:48.000Z","size":263,"stargazers_count":6,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-27T20:59:50.988Z","etag":null,"topics":["composer-library","csv","csv-parser","data","json-parser","parsing","plugin-architecture","processing","rearrange-array","recordset","regex","regex-match","regex-pattern","repack","repair-processes","reparse","text","text-generation","text-processing","yaml-parser"],"latest_commit_sha":null,"homepage":"https://szczyglis.dev/ultimate-chain-parser","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/szczyglis-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2022-04-21T18:32:10.000Z","updated_at":"2025-02-24T16:45:45.000Z","dependencies_parsed_at":"2024-08-25T21:44:01.940Z","dependency_job_id":"dba1d1cf-9b6e-434b-855e-4ed6a370db4f","html_url":"https://github.com/szczyglis-dev/ultimate-chain-parser","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/szczyglis-dev/ultimate-chain-parser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szczyglis-dev%2Fultimate-chain-parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szczyglis-dev%2Fultimate-chain-parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szczyglis-dev%2Fultimate-chain-parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szczyglis-dev%2Fultimate-chain-parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/szczyglis-dev","download_url":"https://codeload.github.com/szczyglis-dev/ultimate-chain-parser/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szczyglis-dev%2Fultimate-chain-parser/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278903034,"owners_count":26065786,"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","status":"online","status_checked_at":"2025-10-08T02:00:06.501Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["composer-library","csv","csv-parser","data","json-parser","parsing","plugin-architecture","processing","rearrange-array","recordset","regex","regex-match","regex-pattern","repack","repair-processes","reparse","text","text-generation","text-processing","yaml-parser"],"created_at":"2024-11-14T21:15:14.713Z","updated_at":"2025-10-08T06:52:41.143Z","avatar_url":"https://github.com/szczyglis-dev.png","language":"PHP","funding_links":["https://www.buymeacoffee.com/szczyglis"],"categories":[],"sub_categories":[],"readme":"Release: **1.2.13** | build: **2024.08.26** | PHP: **^7.2.5|^8.0**\n\n# Ultimate Chain Parser - advanced chain-flow-based parser\n\n**Ultimate Chain Parser is a modular package designed for chain processing of text data, converting it into structured output.**\nThe application concept is based on processing data in successive iterations using configurable data processing modules. Each module in the execution chain sequentially accesses the output of the preceding module and uses it as input.\n\n## How to install\n```\ncomposer require szczyglis/ultimate-chain-parser\n``` \n**For what purposes can the Ultimate Chain Parser be used?**\n\n- Processing any data broken into inconsistent rows or columns into a standardized format (e.g., CSV).\n- Re-parsing data according to specific complex patterns.\n- Creating datasets that are easy to insert into a database or import into software like Excel.\n- Performing complex text manipulation.\n- ...and many other tasks.\n\n## Live Demo: https://szczyglis.dev/ultimate-chain-parser\n\n![parser2](https://user-images.githubusercontent.com/61396542/164573563-e034b324-37e2-4742-a120-fd8a90324708.png)\n\n# Features\n\n- Re-parsing poorly arranged data into structured, clean datasets (e.g., CSV)\n- Parsing poorly arranged or broken data copied from websites, Word documents, or PDFs\n- Running pre-configured tools (plugins) in a sequence\n- Performing complex text data manipulations\n- Parsing complex data using programmable regular expressions executed in a defined sequence\n- Featuring an easy-to-use and powerful configuration system\n- Executing actions through splitting tasks into smaller, separate tools, each performing a different batch of tasks in cooperation with the rest\n- Including tools that can work separately or together: parser, cleaner, limiter, and replacer\n- Offering a modular structure based on a plug-in system, with each element extendable or replaceable by custom implementations; every component has its own interface for extending functionality or replacing existing ones\n- Providing multiple extendable components: configuration providers, input data readers, data parsers, renderers, loggers, etc.\n- Including an HTML/AJAX-based configurator application for real-time testing and configuration\n- Featuring a command-line tool for ease of use\n- Easy integration with modern frameworks like Symfony\n\n# Requirements:\n\n  - PHP 7.2.5+ or PHP 8.0+\n  - Composer - https://getcomposer.org/\n\n# Example of an Action\n\n**Sample text data that requires processing:**\n\n```\n123\nterminator\nschwarzenegger\n\naction movie\n\nvery good\n\n\n456\ntitanic\n\ndicaprio\n\n\nsame director\n                  \n\n```\n\n**Ugly, right? The Ultimate Chain Parser can transform such inconsistently arranged data into a structured format such as CSV, JSON, raw PHP array, or any other schema easily defined by the user:**\n```\n123,terminator,schwarzenegger,action movie very good\n456,titanic,dicaprio,same director\n```\n\n```\n[\n    [\n        {\n            \"id\": \"123\",\n            \"title\": \"terminator\",\n            \"actor\": \"schwarzenegger\",\n            \"description\": \"action movie very good\"\n        },\n        {\n            \"id\": \"456\",\n            \"title\": \"titanic\",\n            \"actor\": \"dicaprio\",\n            \"description\": \"same director\"\n        }\n    ]\n]\n```\n\nThe above CSV and JSON data has been generated completely automatically using only a few configuration options provided in the parser input. The main concept behind the operation is to run a set of processing tools (called Plugins) in a chain. Each successively started process accesses the output from the previous process in the chain. Each of these chain elements can be freely configured with different options. Configuration can be done in many ways: by running Chain Parser directly from your code, loading configuration from an external file and running from the command line, or completely live using the Ajax web form-based configurator included in the package. Ultimate Chain Parser can also directly return a ready (not parsed) dataset prepared from analyzed data (in the form of a PHP array or JSON data).\n\n# Installation\n\n**Composer / packagist**\n\n```\ncomposer require szczyglis/ultimate-chain-parser\n``` \n\n**Manual installation**\n\n- Download the zip package and extract it.\n- Run `composer install` in the project directory to install dependencies.\n- Include the Composer autoloader in your application and instantiate the `ChainParser` object.\n\n\n# Example of use\n\n```php\n  \u003c?php\n\n  // app.php\n\n  require  __DIR__.'/vendor/autoload.php';\n\n  use Szczyglis\\ChainParser\\ChainParser;\n  use Szczyglis\\ChainParser\\Input\\TextInput;\n  use Szczyglis\\ChainParser\\Options\\ArrayOptions;\n\n  $parser = new ChainParser();\n  $parser-\u003esetInput(new TextInput('some text data that needs to be parsed'));\n  $parser-\u003eadd('parser', new ArrayOptions([\n    // options here\n  ]); \n  $parser-\u003erun();\n\n  $result = $parser-\u003erenderOutput();\n  $log = $parser-\u003erenderLog();\n\n  echo $result;\n```\n\n## Live example\n\nGo to https://szczyglis.dev/ultimate-chain-parser to run the online demo, or run **example.php** included in the package to open the AJAX-based demo with the chain configurator in real-time mode. On the page that you see on the screen, you will find the described options, each of which you will be able to use when manually configuring the chain:\n\n### Adding Elements to the Chain\n\n**Manually adding elements to the chain is very easy:**\n\n```php\n  $parser = new ChainParser();  \n  $parser\n    -\u003eadd('cleaner', new ArrayOptions([\n        //options\n    ])\n    -\u003eadd('parser', new ArrayOptions([\n        //options\n    ])\n    -\u003eadd('limiter', new ArrayOptions([\n        //options\n    ]); \n  \n  $parser-\u003erun();\n```\nThe above code adds 3 new elements with defined tools (called Plugins) to the chain. Each of these elements will operate on the output of the previous element. The options are passed as the second argument, wrapped in the option provider class. You can combine the elements in any order and quantity until you achieve the desired result.\n\n**Elements do not have to be added manually as described above. You can use a predefined configuration to programmatically build the defined chain automatically.**\n\n## Configuration, options and usage\n\n### Tool: parser\n\nThe main tool of the application, used to parse data according to specific patterns and rules.\n\n**Options:**\n\n**- use_dataset** - `boolean` Enables operation on a dataset prepared by the previous element, instead of its parsed output. This allows transferring an already prepared set of data between elements. Do not use this in the first element of the chain when starting from raw input, as there is no prepared dataset from a previous element at the beginning.\n\n**- regex_match** - `array` A set of regular expressions to match the data with the corresponding fields. You can add multiple patterns for each field; if more than one pattern is provided, only one needs to match (the logical OR operation is performed). This option can be specified in text (one field per line) or directly in a PHP array.\n\n  *Syntax:* FIELDNAME:/REGEX/ (per line)\n\n  *Example (text):*\n\n    id:/^[\\d]+$/\n    name:/^[^\\d]+/\n    name:/[^\\d]+$/\n\n  *Example (array):*\n  ```php\n    $options['regex_match'] = [\n      'id' =\u003e [\n         0 =\u003e '/^[\\d]+$/',\n      ],\n      'name' =\u003e [\n        0 =\u003e '/^[^\\d]+/',\n        1 =\u003e '/[^\\d]+$/',\n      ],\n    ];\n```\n**- regex_ignore_before** - `array` A list of regular expressions that, if matched (before applying \"replace_filter_before\"), will skip the matched data block. This is used to ignore blocks matching the given pattern. You can enter multiple expressions, either in text form with each expression on a new line, or directly in a PHP array.\n\n  *Syntax:* /REGEX/ (per line)\n\n  *Example (text):*\n\n    /^XYZ+$/\n    /^some unwanted data/\n\n  *Example (array):*\n  ```php\n    $options['regex_ignore_before'] = [\n      0 =\u003e '/^XYZ+$/',\n      1 =\u003e '/^some unwanted data/',\n    ];\n  ```\n**- regex_ignore_after** - `array` A list of regular expressions that, if matched (after applying \"replace_filter_before\"), will skip the matched data block. This is used to ignore blocks matching the given pattern. You can enter multiple expressions, either in text form with each expression on a new line, or directly in a PHP array.\n\n  *Syntax*: /REGEX/ (per line)\n\n  *Example (text):*\n\n    /^XYZ+$/\n    /^some unwanted data/\n\n  *Example (array):*\n  ```php\n    $options['regex_ignore_after'] = [\n      0 =\u003e '/^XYZ+$/',\n      1 =\u003e '/^some unwanted data/',\n    ];\n  ```\n**- replace_field_before** - `array` A list of regular expressions used to replace or precondition a data block with another, applied before each attempt to match a given field. This can be used to pre-filter the data before each match attempt. You can enter multiple expressions, either in text form with each expression on a new line, or directly in a PHP array.\n\n  *Syntax:* FIELDNAME:/REGEX/ =\u003e \"REPLACED STRING\" (one pattern per line)\n\n  *Example (text):*\n\n    id:/^[\\d]+$/ =\u003e 12345\n    name:/^([^\\d]+)/ =\u003e $1\n    name:/^([A-Z]+)/ =\u003e abc$1\n\n  *Example (array):*\n  ```php\n    $options['replace_field_before'] = [\n      'id' =\u003e [\n        0 =\u003e [\n          'pattern' =\u003e '/^[\\d]+$/',\n          'replacement' =\u003e '12345',\n        ],        \n      ],\n      'name' =\u003e [\n        0 =\u003e [\n          'pattern' =\u003e '/^([^\\d]+)/',\n          'replacement' =\u003e '$1',\n        ], \n        1 =\u003e [\n          'pattern' =\u003e '/^([A-Z]+)/',\n          'replacement' =\u003e 'abc$1',\n        ],        \n      ],\n    ];\n  ``` \n**- replace_field_after** - `array` A list of regular expressions to replace an already matched field with another text string. This can be used for post-processing of matched fields. You can enter multiple expressions, either in text form with each expression on a new line, or directly in a PHP array.\n\n  *Syntax:* FIELDNAME:/REGEX/ =\u003e \"REPLACED STRING\" (one pattern per line)\n\n  *Example (text):*\n\n    id:/^[\\d]+$/ =\u003e 12345\n    name:/^([^\\d]+)/ =\u003e $1\n\n  *Example (array):*\n  ```php\n    $options['replace_field_after'] = [\n      'id' =\u003e [\n        0 =\u003e [\n          'pattern' =\u003e '/^[\\d]+$/',\n          'replacement' =\u003e '12345',\n        ],        \n      ],\n      'name' =\u003e [\n        0 =\u003e [\n          'pattern' =\u003e '/^([^\\d]+)/',\n          'replacement' =\u003e '$1',\n        ],        \n      ],\n    ];\n```\n**- replace_block_before** - `array` A list of regular expressions to replace or pre-prepare a data block with another, applied to the entire data block before attempting to match. This can be used to pre-filter the data before each match attempt. You can enter multiple expressions, either in text form with each expression on a new line, or directly in a PHP array.\n  \n  *Syntax:* /REGEX/ =\u003e \"REPLACED STRING\" (one pattern per line).\n\n  *Example (text):*\n\n    /^[\\d]+$/ =\u003e 12345\n    /^([^\\d]+)/ =\u003e $1\n\n  *Example (array):*\n  ```php\n    $options['replace_block_before'] = [\n      0 =\u003e [\n        'pattern' =\u003e '/^[\\d]+$/',\n        'replacement' =\u003e '12345',\n      ]\n      1 =\u003e [\n        'pattern' =\u003e '/^([^\\d]+)/',\n        'replacement' =\u003e '$1',\n      ],\n    ];\n```\n**- replace_block_after** - `array` A list of regular expressions to replace an already matched block with another text string. Applied to the entire block of data after a match is made, this can be used for post-processing of matched data. You can enter multiple expressions, either in text form with each expression on a new line, or directly in a PHP array.\n\n  *Syntax:* /REGEX/ =\u003e \"REPLACED STRING\" (one pattern per line)\n\n  *Example (text):*\n\n    /^[\\d]+$/ =\u003e 12345\n    /^([^\\d]+)/ =\u003e $1\n\n  *Example (array):*\n  ```php\n    $options['replace_block_after'] = [\n      0 =\u003e [\n        'pattern' =\u003e '/^[\\d]+$/',\n        'replacement' =\u003e '12345',\n      ]\n      1 =\u003e [\n        'pattern' =\u003e '/^([^\\d]+)/',\n        'replacement' =\u003e '$1',\n      ],\n    ];\n```\n**- fields** - `array` A list of fields to be matched. Enter the names of the fields into which you want to divide the parsed data, e.g., id, name, actor, description. The fields should be entered on one line, separated by commas (,), or as an array of fields.\n\n  *Syntax:* FIELDNAME1,FIELDNAME2,FIELDNAME3,FIELDNAME4...\n\n  *Example (text):*\n\n    id,title,actor,description\n\n  *Example (array):*\n  ```php\n    $options['fields'] = [\n      'id',\n      'title',\n      'actor',\n      'description',\n    ];\n```\n**- output_fields** - `array` A list of fields to match from the list above to be displayed in the output, e.g., id, name, actor. The fields should be entered on one line, separated by commas (,), or as an array of fields.\n\n  *Syntax:* FIELDNAME1,FIELDNAME2,FIELDNAME3,FIELDNAME4...\n\n  *Example (text):*\n\n    id,title,actor,description\n\n  *Example (array):*\n  ```php\n    $options['output_fields'] = [\n      'id',\n      'title',\n      'actor',\n      'description',\n    ];\n\n```\n**- sep_input_rowset** - `string` Separator for rowsets for input when splitting into rowsets, used depending on the expected output distribution, e.g., \\n.\n\n**- sep_input_row** - `string` Separator for rows in input data when splitting into rows, used depending on the expected output distribution, e.g., \\n.\n\n**- sep_input_column** - `string` Separator for columns in input data when splitting into columns, used depending on the expected output distribution, e.g., a comma (,).\n\n**- sep_output_rowset** - `string` Separator for rowsets in output when joining results from rowsets, used depending on the desired output format, e.g., \\n.\n\n**- sep_output_row** - `string` Separator for rows in output when joining results from rows, used depending on the desired output format, e.g. \\n.\n\n**- sep_output_column** - `string` Separator for columns in output when joining results from columns, used depending on the desired output format, e.g. comma (,).\n\n**- empty_field_placeholder** - `string` Placeholder to replace the given field if the matched field is empty. Leave blank if you do not want to use any placeholders.\n\n**- is_debug** - `boolean`, If TRUE, append debugger information to each line of output.\n\n**- is_empty_field_placeholder** - `boolean` If TRUE, replace empty spaces in the matched fields with the string specified in the `empty_placeholder` option.\n\n___\n\n## Tool: cleaner\n\nA tool for cleaning, sanitizing, and pre-preparing input data for further processing.\n\n**Options:**\n\n**- use_dataset** - `boolean`, Enables operation on a dataset prepared by the previous element, instead of its parsed output. This allows transferring an already prepared set of data between elements. Do not use this in the first element of the chain when starting from raw input, as there is no prepared dataset from a previous element at the beginning.\n\n**- trim** - `boolean` Applies the `trim()` function to every block.\n\n**- clean_blocks** - `boolean` Removes empty blocks.\n\n**- fix_newlines** - `boolean` Replaces all \\r\\n with \\n.\n\n**- strip_tags** - `boolean` Applies the `strip_tags()` function to all.\n\n**- sep_input_rowset** - `string` Separator for rowsets for input when splitting into rowsets, used depending on the expected output distribution, e.g., \\n.\n\n**- sep_input_row** - `string` Separator for rows in input data when splitting into rows, used depending on the expected output distribution, e.g., \\n.\n\n**- sep_input_column** - `string` Separator for columns in input data when splitting into columns, used depending on the expected output distribution, e.g., a comma (,).\n\n**- sep_output_rowset** - `string` Separator for rowsets in output when joining results from rowsets, used depending on the desired output format, e.g., \\n.\n\n**- sep_output_row** - `string` Separator for rows in output when joining results from rows, used depending on the desired output format, e.g. \\n.\n\n**- sep_output_column** - `string` Separator for columns in output when joining results from columns, used depending on the desired output format, e.g. comma (,).\n\n___\n\n\n## Tool: limiter\n\nA tool for limiting the amount of generated or received data according to specific patterns and rules. It can also be used for deleting data.\n\n**Options:**\n\n**- use_dataset** - `boolean` Enables operation on a dataset prepared by the previous element, instead of its parsed output. This allows transferring an already prepared set of data between elements. Do not use this in the first element of the chain when starting from raw input, as there is no prepared dataset from a previous element at the beginning.\n\n**- data_mode** - `string` `[rowset|row|column]` - Selects the dimension on which the other options will operate.\n\n**- interval_allow** - `integer` Restricts output to blocks that match the given interval. Default is 1.\n\n**- range_allow** - `array` Limits output to blocks that match specified ranges; leave empty to allow all blocks. Specify range(s) separated by commas, with indexing starting from 0.\n\n  *Syntax:* integer1, integer2, integer3-integer4,integer5-,-integer6 [...]\n\n  *Example (text):*\n\n    0, 3, 5-7, 15-, -20\n\n  *Example (array):*\n  ```php\n    $options['range'] = [\n      0 =\u003e 0,\n      1 =\u003e 3,\n      2 =\u003e [\n        'from' =\u003e 5\n        'to' =\u003e 7,\n      ],\n      3 =\u003e [\n        'from' =\u003e 15,\n        'to' =\u003e null,\n      ],\n      4 =\u003e [\n        'from' =\u003e null,\n        'to' =\u003e 20,\n      ],\n    ];\n  ```\n**- regex_allow** - `array` Restricts output to blocks that match the given regular expressions. You can enter multiple expressions, each on a new line.\n\n  *Syntax:* /REGEX/ (per line)\n\n  *Example (text):*\n\n    /^XYZ+$/\n    /^ZYX+$/\n\n  *Example (array):*\n  ```php\n    $options['regex_allow'] = [\n      0 =\u003e '/^XYZ+$/',\n      1 =\u003e '/^ZYX+$/',\n    ];\n\n  ```\n**- interval_deny** - `integer` Restricts output to blocks that do not match the given interval. Default is 1.\n\n**- range_deny** - `array`, Limits blocks in output to those that do not match specified ranges. Leave empty to allow all blocks, or specify range(s) separated by commas. Indexing starts from 0.\n\n  *Syntax:* integer1, integer2, integer3-integer4,integer5-,-integer6 [...]\n\n  *Example (text):*\n\n    0, 3, 5-7, 15-, -20\n\n  *Example (array):*\n  ```php\n    $options['range'] = [\n      0 =\u003e 0,\n      1 =\u003e 3,\n      2 =\u003e [\n        'from' =\u003e 5\n        'to' =\u003e 7,\n      ],\n      3 =\u003e [\n        'from' =\u003e 15,\n        'to' =\u003e null,\n      ],\n      4 =\u003e [\n        'from' =\u003e null,\n        'to' =\u003e 20,\n      ],\n    ];\n  ```\n**- regex_deny** - `array` Restricts output to blocks that do not match the given regular expressions. You can enter multiple expressions, each on a new line.\n\n  *Syntax:* /REGEX/ (per line)\n\n  *Example (text):*\n\n    /^XYZ+$/\n    /^ZYX+$/\n\n  *Example (array):*\n  ```php\n    $options['regex_deny'] = [\n      0 =\u003e '/^XYZ+$/',\n      1 =\u003e '/^ZYX+$/',\n    ];\n\n  ```\n**- sep_input_rowset** - `string` Separator for rowsets for input when splitting into rowsets, used depending on the expected output distribution, e.g., \\n.\n\n**- sep_input_row** - `string` Separator for rows in input data when splitting into rows, used depending on the expected output distribution, e.g., \\n.\n\n**- sep_input_column** - `string` Separator for columns in input data when splitting into columns, used depending on the expected output distribution, e.g., a comma (,).\n\n**- sep_output_rowset** - `string` Separator for rowsets in output when joining results from rowsets, used depending on the desired output format, e.g., \\n.\n\n**- sep_output_row** - `string` Separator for rows in output when joining results from rows, used depending on the desired output format, e.g. \\n.\n\n**- sep_output_column** - `string` Separator for columns in output when joining results from columns, used depending on the desired output format, e.g. comma (,).\n___\n\n## Tool: replacer\n\nA tool for converting specific batches of data to other formats according to defined patterns and rules.\n\n**Options:**\n\n**- use_dataset** - `boolean` Enables operation on a dataset prepared by the previous element, instead of its parsed output. This allows transferring an already prepared set of data between elements. Do not use this in the first element of the chain when starting from raw input, as there is no prepared dataset from a previous element at the beginning.\n\n**- data_mode** - `string` `[rowset|row|column]` Selects the dimension on which the other options will operate.\n\n**- regex** - `array` Regular expressions to replace the appropriate strings. You can enter multiple patterns, each on a new line.\n\n  *Syntax:* /REGEX/ =\u003e \"REPLACED STRING\" (one pattern per line)\n\n  *Example (text):*\n\n    /^[\\d]+$/ =\u003e 12345\n    /^([^\\d]+)/ =\u003e $1\n\n  *Example (array):*\n  ```php\n    $options['regex'] = [\n      0 =\u003e [\n        'pattern' =\u003e '/^[\\d]+$/',\n        'replacement' =\u003e '12345',\n      ]\n      1 =\u003e [\n        'pattern' =\u003e '/^([^\\d]+)/',\n        'replacement' =\u003e '$1',\n      ],\n    ];\n  ```\n\n**- interval** - `integer` Limits replacing to a specific interval. Default is 1.\n\n**- range** - `array` Limits blocks to replace to specified ranges; leave empty to replace all blocks. Specify range(s) separated by commas. Indexing starts from 0.\n\n  *Syntax:* integer1, integer2, integer3-integer4,integer5-,-integer6 [...]\n\n  *Example (text):*\n\n    0, 3, 5-7, 15-, -20\n\n  Example (array):\n  ```php\n    $options['range'] = [\n      0 =\u003e 0,\n      1 =\u003e 3,\n      2 =\u003e [\n        'from' =\u003e 5\n        'to' =\u003e 7,\n      ],\n      3 =\u003e [\n        'from' =\u003e 15,\n        'to' =\u003e null,\n      ],\n      4 =\u003e [\n        'from' =\u003e null,\n        'to' =\u003e 20,\n      ],\n    ];\n  ```\n\n**- sep_input_rowset** - `string` Separator for rowsets for input when splitting into rowsets, used depending on the expected output distribution, e.g., \\n.\n\n**- sep_input_row** - `string` Separator for rows in input data when splitting into rows, used depending on the expected output distribution, e.g., \\n.\n\n**- sep_input_column** - `string` Separator for columns in input data when splitting into columns, used depending on the expected output distribution, e.g., a comma (,).\n\n**- sep_output_rowset** - `string` Separator for rowsets in output when joining results from rowsets, used depending on the desired output format, e.g., \\n.\n\n**- sep_output_row** - `string` Separator for rows in output when joining results from rows, used depending on the desired output format, e.g. \\n.\n\n**- sep_output_column** - `string` Separator for columns in output when joining results from columns, used depending on the desired output format, e.g. comma (,).\n___\n\n# Running in command line (PHP CLI)\n\nThe package includes a Symfony command in the `Command` directory. You can run the command with the script `cmd.php`:\n\n**Usage in CLI:**\n\n  ```\n  ./cmd.php chainparser /path/to/data /path/to/config.yaml [--options]\n  ```\n\n**Arguments:**\n\n`/path/to/data` - path to the file with text data to parse.\n\n`/path/to/config.yaml` - path to the file with the YAML config.\n\n**Options:**\n\n  --log=0   - disable log output\n\n  --data=0  - disable raw data output\n\nThe package contains 2 example files:\n\n  - example.txt\n\n  - example.yaml\n\nYou can use these example files for testing:\n\n  ```\n  ./cmd.php chainparser ./example.txt ./example.yaml\n  ```\n\nDisable log and data output, leaving only the parse result output:\n\n  ```\n  ./cmd.php chainparser ./example.txt ./example.yaml --log=0 --data=0\n  ```\n\nStore the output result in a file named `output.txt`:\n\n  ```\n  ./cmd.php chainparser ./example.txt ./example.yaml --log=0 --data=0 \u003e output.txt\n  ```\n\n\n# Config options\n\nYou can use the included config provider or write your own. Currently, two config providers are included:\n\n- **ArrayConfig** - works with the configuration provided directly from a PHP array.\n\n- **YamlConfig** - works with the configuration read from a YAML file.\n\n### Usage\n```php  \n\u003c?php\n\nnamespace App;\n\nuse Szczyglis\\ChainParser\\Config\\YamlConfig;\n\n\n//...\n\n$parser-\u003esetConfig(new YamlConfig('/path/to/config.yaml'));\n```\nor\n```php\n\u003c?php\n\nnamespace App;\n\nuse Szczyglis\\ChainParser\\Config\\ArrayConfig;\n\n//...\n\n$parser-\u003esetConfig(new ArrayConfig([\n  'key' =\u003e 'value',\n]));\n```\n\n### Available configuration options\n\n**- full_output** - `boolean`, default: false. If true, all outputs from all chain elements are rendered in the output. If false, only the last result is rendered.\n\n**- log_file** - `string`, absolute path to the logfile if using PsrLogger (Monolog).\n\n\n**- no_log** - bool, if true, logging is disabled.\n\n\n# Loggers\n\nYou can use the included loggers or write your own. The package includes three different loggers:\n\n- **ArrayLogger** - logs everything to a PHP array.\n\n- **ConsoleLogger** - logs output to the CLI console.\n\n- **PsrLogger** - stores logs in a file using Monolog Logger.\n\n\n### Usage\n```php\n\u003c?php\n\nnamespace App;\n\nuse Szczyglis\\ChainParser\\Logger\\ArrayLogger;\nuse Szczyglis\\ChainParser\\Logger\\PsrLogger;\nuse Szczyglis\\ChainParser\\Logger\\ConsoleLogger;\n\n\n//...\n\n$parser-\u003eaddLogger(new ArrayLogger());\n$parser-\u003eaddLogger(new PsrLogger('/path/to/logfile'));\n$parser-\u003eaddLogger(new ConsoleLogger());\n\n```\n\nYou can register multiple loggers at once; the output will be sent to all of them simultaneously.\n\nAccessing the generated logs is very simple:\n\n```php  \n  ...\n$parser-\u003erun();\n\n$log = $parser-\u003erenderLog();\n\ndump($log);\n```\nOr if you want to get raw (not rendered/parsed) log data, use:\n```php\n$parser-\u003erun();\n\n$output = $parser-\u003egetOutput();\n$log = $output-\u003eget('logs');\ndump($log);\n```\n\n___\n\n# Extending Chain Parser\n\nThe concept is based on full modularity, allowing you to extend the package with your own parsers and features. \nEach element can be adapted to meet your needs and solve your specific problems.\n\n## Configuration providers\n\nConfiguration providers are responsible for reading and parsing the configuration. There are a few base configuration providers included in the package, but you can easily create your own.\n\nConfiguration providers are located in: `Szczyglis\\ChainParser\\Config` namespace.\n\n### Included configuration providers\n\n- **ArrayConfig** - reads configuration from a PHP array passed as a constructor argument.\n\n- **YamlConfig** - reads configuration from a YAML file (path to the file needs to be passed as a constructor argument).\n\n### How to create your own configuration provider\n\n- implement the interface `Szczyglis\\ChainParser\\Contract\\ConfigInterface`\n- attach your config provider using the `setConfig()` method\n\n\n### Example\n\n```php  \n\u003c?php\n\n// MyConfig.php\n\nnamespace App;\n\nuse Szczyglis\\ChainParser\\Contract\\ConfigInterface;\nuse Szczyglis\\ChainParser\\Helper\\AbstractInput;\n\nclass MyConfig implements ConfigInterface\n{\n  private $config;\n\n  public function __construct(array $config)\n  {\n    $this-\u003econfig = $config;\n  }\n\n  public function get(string $key)\n  {\n    if (array_key_exists($key, $this-\u003econfig)) {\n      return $this-\u003econfig[$key];\n    }   \n  }\n\n  public function set(string $key, $value)\n  {\n    $this-config[$key] = $value;    \n  }\n\n  public function has(string $key)\n  {\n    if (array_key_exists($key, $this-\u003econfig)) {\n      return true;\n    }   \n  }\n\n  public function all()\n  {\n    return $this-\u003econfig;\n  }\n}\n\n```\n```php\n\n\u003c?php\n\n// app.php\n\nuse Szczyglis\\ChainParser\\ChainParser;\nuse App\\MyConfig;\n\n$parser = new ChainParser();\n$parser-\u003esetConfig(new MyConfig([\n  'foo' =\u003e 'bar',\n]));\n\n// rest of initialization, config, etc...\n\n$parser-\u003erun();\n$parser-\u003erenderOutput();\n```\n___\n\n\n## Input data providers\n\nInput data providers are responsible for reading input data. There are a few base input data providers included in the package, but you can easily create your own.\n\nInput data providers are located in: `Szczyglis\\ChainParser\\Input` namespace.\n\n### Included input data providers\n\n- **TextInput** - reads input data directly from a text string passed as a constructor argument.\n\n- **FileInput** - reads input data from a file (path to the file needs to be passed as a constructor argument).\n\n### How to create your own input data provider\n\n- implement the interface `Szczyglis\\ChainParser\\Contract\\InputInterface`\n- optionally extend the abstract helper class `Szczyglis\\ChainParser\\Helper\\AbstractInput`\n- attach your input data provider using the `setInput()` method\n\n\n### Example\n\n```php  \n\u003c?php\n\n// MyInput.php\n\nnamespace App;\n\nuse Szczyglis\\ChainParser\\Contract\\InputInterface;\nuse Szczyglis\\ChainParser\\Helper\\AbstractInput;\n\nclass MyInput extends AbstractInput implements InputInterface\n{\n  private $input;\n  private $dataset = [];\n\n  public function __construct(string $input)\n  {\n    $this-\u003einput = $input;\n  }\n\n  public function getInput()\n  {\n    return $this-\u003einput;\n  }\n\n  public function getDataset()\n  {\n    return $this-\u003edataset;\n  }\n}\n\n```\n```php\n\n\u003c?php\n\n// app.php\n\nuse Szczyglis\\ChainParser\\ChainParser;\nuse App\\MyInput;\n\n$parser = new ChainParser();\n$parser-\u003esetInput(new MyInput('some input data here...'));\n\n// rest of initialization, config, etc...\n\n$parser-\u003erun();\n$parser-\u003erenderOutput();\n```\n___\n\n## Loggers\n\nLoggers are responsible for logging data from plugins. There are a few base loggers included in the package, but you can easily create your own.\n\nLoggers are located in the `Szczyglis\\ChainParser\\Logger` namespace.\n\n### Included loggers\n\n- **ArrayLogger** - writes logs directly to a PHP array.\n\n- **PsrLogger** - writes logs to a file using Monolog.\n\n- **ConsoleLogger** - displays logs directly to the console.\n\n### How to create your own logger\n\n- implement the interface `Szczyglis\\ChainParser\\Contract\\LoggerInterface`\n- optionally extend the abstract helper class `Szczyglis\\ChainParser\\Helper\\AbstractLogger`\n- add a logger using the `addLogger()` method\n\n\n### Example\n\n```php  \n\u003c?php\n\n// MyLogger.php\n\nnamespace App;\n\nuse Szczyglis\\ChainParser\\Contract\\LoggerInterface;\nuse Szczyglis\\ChainParser\\Helper\\AbstractLogger;\n\nclass MyLogger extends AbstractLogger implements LoggerInterface\n{\n  private $logs;\n\n  public function addMessage(string $message, array $data)\n  {\n    $this-\u003elogs[] = $message;\n  }\n\n  // ...rest of code\n}\n\n```\n```php\n\n\u003c?php\n\n// app.php\n\nuse Szczyglis\\ChainParser\\ChainParser;\nuse App\\MyLogger;\n\n$parser = new ChainParser();\n$parser-\u003eaddLogger(new MyLogger());\n\n// rest of initialization, config, etc...\n\n$parser-\u003erun();\n$parser-\u003erenderOutput();\n```\n___\n\n## Options providers\n\nOptions providers are responsible for reading, parsing, and serving the plugin's options. There are a few base options providers included in the package, but you can easily create your own.\n\nOptions providers are located in the `Szczyglis\\ChainParser\\Options` namespace.\n\n### Included options providers\n\n- **ArrayOptions** - reads options directly from a PHP array passed as a constructor argument.\n\n- **FormOptions** - parses options passed as text or from an HTML form.\n\n### How to create your own options provider\n\n- implement the interface `Szczyglis\\ChainParser\\Contract\\OptionsInterface`\n- optionally extend the abstract helper class `Szczyglis\\ChainParser\\Helper\\AbstractOptions`\n- initialize the plugin using your own options provider\n\n### Example\n\n```php  \n\u003c?php\n\n// MyOptions.php\n\nnamespace App;\n\nuse Szczyglis\\ChainParser\\Contract\\OptionsInterface;\nuse Szczyglis\\ChainParser\\Helper\\AbstractOptions;\n\nclass MyOptions extends AbstractOptions implements OptionsInterface\n{\n  private $options;\n\n  public function __construct(string $options)\n  {\n    $this-\u003eoptions = $options;\n  }\n\n  public function get(string $key)\n  {\n    if (array_key_exists($key, $this-\u003eoptions)) {\n      return $this-\u003eoptions[$key];\n    }   \n  }\n\n  public function has(string $key)\n  {\n    if (array_key_exists($key, $this-\u003eoptions)) {\n      return true;\n    }   \n  }\n\n  public function all()\n  {\n    return $this-\u003eoptions;\n  }\n}\n\n```\n```php\n\u003c?php\n\n// app.php\n\nuse Szczyglis\\ChainParser\\ChainParser;\nuse App\\MyOptions;\n\n$parser = new ChainParser();\n$parser-\u003eadd('parser', new MyOptions([\n    'foo' =\u003e 'bar',\n]));\n\n// rest of initialization, config, etc...\n\n$parser-\u003erun();\n$parser-\u003erenderOutput();\n```\n\n___\n\n## Option resolvers\n\nOption resolvers are responsible for parsing options, e.g., when options are provided using an HTML form and need to be parsed into an array. There are a few base option resolvers included in the package, but you can easily create your own.\n\nOption resolvers are located in the `Szczyglis\\ChainParser\\OptionResolver` namespace.\n\n### Included options resolvers\n\n- **SingleLineResolver** - parses options from a single line.\n\n- **MultiLineResolver** - parses more complex syntax from multiple lines.\n\n- **RangeResolver** - parses range-based options, e.g., 1,2,8-10.\n\n### How to create your own option resolver, e.g. for use with own plugin configuration\n\n- implement the interface `Szczyglis\\ChainParser\\Contract\\OptionResolverInterface`\n- register the option resolver using `addResolver()` method\n\n\n### Example\n\n``` php\n\u003c?php\n\n// MyResolver.php\n\nnamespace App;\n\nuse Szczyglis\\ChainParser\\Contract\\OptionResolverInterface;\nuse Szczyglis\\ChainParser\\Helper\\AbstractInput;\n\nclass MyResolver implements OptionResolverInterface\n{\n  public function resolve(string $key, $value)\n  {\n    return explode(';', $value);    \n  }\n\n  public function getName(): string\n  {\n    return 'my_resolver';\n  }\n}\n```\n\n\n```php\n\u003c?php\n\n// MyPlugin.php\n\n\nclass MyPlugin ...\n\npublic function run(): bool\n{\n  // ...\n}\n\npublic function registerOptions(): array\n{\n    return [\n        'my_resolver' =\u003e [  // resolver name\n            'foo', // option name\n        ],\n    ];\n}\n```\n\n\n```php\n\u003c?php\n\n// app.php\n\nuse Szczyglis\\ChainParser\\ChainParser;\nuse Szczyglis\\ChainParser\\Options\\FormOptions;\nuse App\\MyResolver;\n\n$parser = new ChainParser();\n$parser-\u003eaddResolver(new MyResolver());\n$parser-\u003eadd('parser', new FormOptions([\n    'foo' =\u003e 'bar1;bar2;bar3', // option \"foo\" will be parsed with your resolver\n]));\n\n// rest of initialization, config, etc...\n\n$parser-\u003erun();\n$parser-\u003erenderOutput();\n```\n\n___\n\n## Plugins\n\nPlugins are the core of the application. They are tools that run along the chain and operate on data. Each plugin can work with the raw input and the output from the previous plugin in the chain. There are also special classes called `Workers` that help you organize your code and break it down into different \"subprocesses.\" Workers share the same data set as the main plugin and can quickly exchange their data with the plugin. You can easily register your own workers using the `registerWorker` method.\n\nPlugins are located in the `Szczyglis\\ChainParser\\Plugin` namespace.\n\n### Included plugins\n\n- **Parser** - The main tool of the application, used to parse data according to specific patterns and rules.\n\n- **Cleaner** - A tool for cleaning the input data, sanitizing and pre-preparing data for further processing.\n\n- **Limiter** - A tool for limiting and removing the amount of generated or received data according to specific patterns and rules.\n\n- **Replacer** - A tool for converting specific batches of data to other formats according to specific patterns and rules.\n\n### How to create your own plugin\n\n- implement the interface `Szczyglis\\ChainParser\\Contract\\PluginInterface`\n- extend the abstract helper class `Szczyglis\\ChainParser\\Helper\\AbstractPlugin`\n- add the plugin to the chain\n\n\n### Example\n```php\n\u003c?php\n\n// MyPlugin.php\n\nnamespace App;\n\nuse Szczyglis\\ChainParser\\Contract\\PluginInterface;\nuse Szczyglis\\ChainParser\\Contract\\LoggableInterface;\nuse Szczyglis\\ChainParser\\Helper\\AbstractPlugin;\n\nclass MyPlugin extends AbstractPlugin implements PluginInterface, LoggableInterface\n{\n  const NAME = 'my_plugin';\n\n  public function run(): bool\n  {\n    $dataset = $this-\u003egetDataset(); // get previous data or from input\n\n    // do something with data\n    \n    $this-\u003esetDataset($dataset); // return data to next element or to output\n\n    return true;\n  }\n\n  public function getName(): string\n  {\n    return self::NAME;\n  }\n}\n```\n```php\n\u003c?php\n\n// app.php\n\nuse Szczyglis\\ChainParser\\ChainParser;\nuse Szczyglis\\ChainParser\\Input\\TextInput;\nuse Szczyglis\\ChainParser\\Options\\ArrayOptions;\nuse App\\MyPlugin;\n\n$parser = new ChainParser();\n$parser-\u003eaddPlugin(new MyPlugin());\n$parser-\u003esetInput(new TextInput('foo'));\n$parser-\u003eadd('my_plugin', new ArrayOptions([\n    'option' =\u003e 'value',\n]));\n\n// rest of initialization, config, etc...\n\n$parser-\u003erun();\necho $parser-\u003erenderOutput(); // returns \"Hello foo\"\n\n``` \n\n### Creating own Worker\n```php\n\u003c?php\n\n// MyWorker.php\n\nnamespace App;\n\nuse Szczyglis\\ChainParser\\Contract\\WorkerInterface;\nuse Szczyglis\\ChainParser\\Contract\\LoggableWorkerInterface;\nuse Szczyglis\\ChainParser\\Helper\\AbstractWorker;\n\nclass MyWorker extends AbstractWorker implements WorkerInterface, LoggableWorkerInterface\n{\n  public function doSomeJob()\n  {\n    $var = $this-\u003egetVar('foo');\n    $var++;\n    $this-\u003esetVar('foo', $var);\n  }\n}\n\n```\n```php\n\u003c?php\n\n// MyPlugin.php\n\nnamespace App;\n\nuse Szczyglis\\ChainParser\\Contract\\PluginInterface;\nuse Szczyglis\\ChainParser\\Contract\\LoggableInterface;\nuse Szczyglis\\ChainParser\\Helper\\AbstractPlugin;\n\nclass MyPlugin extends AbstractPlugin implements PluginInterface, LoggableInterface\n{\n  const NAME = 'my_plugin';\n\n  public function run(): bool\n  {\n    $worker = $this-\u003egetWorker('my_worker');\n\n    $foo = 10;\n    $this-\u003esetVar('foo', $foo);\n\n    $worker-\u003edoSomeJob();\n\n    $bar = $this-\u003egetVar('foo');\n\n    echo $bar; // will display 11\n\n    return true;\n  }\n\n  public function registerWorkers(): array\n  {\n    return [\n      'my_worker' =\u003e new MyWorker(),\n    ];\n  }\n}\n```\n\nA worker is registered and initiated automatically as soon as it is given in the `registerWorkers()` method. As the example above showed, workers and plugins have a common container for temporary data used to exchange variables between the plugin and the worker:\n```php\n$this-\u003esetVar('foo', $bar); // sets var foo \n$foo = $this-\u003egetVar('foo'); // gets var foo\n```\n\n\n### Access to input and output data from the plugin and worker level is as follows:\n\n```php\n\u003c?php\n\n// MyPlugin.php\n\nclass MyPlugin ...\n\npublic function run(): bool\n{\n  $input = $this-\u003egetPrev('output'); // output from previous element in chain (or raw input if Plugin is first in chain)\n  $prevDataset = $this-\u003egetPrev('dataset'); // output data (as `array`, not parsed) from previous element in chan\n  $dataset = $this-\u003eget('dataset'); // get current dataset or output from previous element\n\n  $rawInput = $this-\u003eget('input'); // raw, initial input\n\n  $i = $this-\u003egetIteration(); // current iteration index in chain\n  $config = $this-\u003egetConfig(); // returns config object\n\n  $optionValue = $this-\u003egetOption('key'); // returns option value by key\n  $allOptions = $this-\u003egetOptions(); // returns all options (as key =\u003e value array)\n\n  $dataset = $this-\u003egetDataset(); // alias for $this-\u003eget('dataset');\n\n  return true;\n}\n\n```\n\n### Setting output data inside the Tool:\n\n```php\n\u003c?php\n\n// MyPlugin.php\n\n\nclass MyPlugin ...\n\npublic function run(): bool\n{\n  $input = $this-\u003egetPrev('output');  // get parsed previous output or current input\n  $dataset = $this-\u003egetDataset(); // get data form previous output or current input\n\n  // do manipulation on data\n\n  $this-\u003esetDataset($dataset);  // data will be sent to the next element in the chain as its input\n\n  return true;\n}\n```\n\n### Helpers inside Plugin and Worker\n\nYou can use some included helpers provided by the `AbstractPlugin` and `AbstractWorker` abstract classes. When you extend your class from them, you have access to some useful methods:\n\n`$this-\u003eisPattern($pattern): bool` - checks if `$pattern` is a valid regex pattern.\n\n`$this-\u003echeckPatterns(array $patterns, string $string): bool` - checks if at least one of the given regex patterns matches a string.\n\n`$this-\u003eapplyPatterns(array $patterns, string $string): string` - applies any replacement patterns to the given string.\n\n`$this-\u003eexplode(string $separator, ?string $input): array` - wrapper for `explode()`, allowing explosion using a regular expression as the separator.\n\n`$this-\u003eimplode(string $joiner, array \u0026$ary): string` - wrapper for `implode()`.\n\n`$this-\u003estrReplace($from, $to, $data): string` - wrapper for `str_replace()`.\n\n`$this-\u003estripTags($data, $tags = null): string` - wrapper for `strip_tags()`.\n\n`$this-\u003etrim($input): string` - wrapper for `trim()`.\n\n`$this-\u003einRange(array $ranges, int $i): bool` - checks if a number matches the given ranges.\n\n`$this-\u003emakeDataset(?string $input, string $sepRowset, string $sepRow, string $sepCol): array` - converts a string input into a 3-dimensional dataset array using the given separators.\n\n`$this-\u003epackDataset(array $dataset, string $sepRowset, string $sepRow, string $sepCol): string` - builds a parsed result from the dataset.\n\n`$this-\u003eiterateDataset(array $dataset, callable $callback): array` - applies a callback to every block in the dataset and returns the dataset modified by the callback. Example:\n\n```php\n\n  $dataset = $this-\u003eiterateDataset($dataset, function($value) {\n      return str_replace('A', 'B', $value);\n  });\n```\nDoing this will cause the character A to be replaced with the character B for each element in the dataset.\n\n**It is a good idea to check the code of the plugins included in the package to see live examples.**\n\n### Registering options that require prior parsing\n\nIf you need to use options that require the data to be parsed first, e.g., into an `array`, use ready-made option resolvers or create your own resolver. To register an option with the appropriate option resolver, return its name in the array using the `registerOptions()` method. From now on, it will be parsed by the assigned resolver. The array should include the name of the resolver and a list of options assigned to it. Example of use:\n\n```php\n\u003c?php\n\n// MyPlugin.php\n\n\nclass MyPlugin ...\n\npublic function run(): bool\n{\n  // ...\n}\n\npublic function registerOptions(): array\n{\n    return [\n        'multiline' =\u003e [  // resolver name\n            'my_pattern', // option name\n        ],\n        'range' =\u003e [ // resolver name\n            'my_range', // option name\n        ],\n    ];\n}\n```\n\nIncluded resolvers:\n\n- **singleline** - parses single-line parameters, such as `key: value =\u003e assignment/replacement`\n\n- **multiline** - parses multi-line parameters, where `key: value =\u003e assignment/replacement` is placed on separate lines.\n\n- **range** - parses range parameters, such as: `1,5,10-20,15-,-80`\n\n\n### Logging messages\n\nFrom the Plugin and Worker level, you have access to log events using registered loggers. The `$this-\u003elog()` method is used for this purpose. To use event logging, you must implement the following interfaces:\n\n- in plugin: `Szczyglis\\ChainParser\\Contract\\LoggableInterface`\n- in worker: `Szczyglis\\ChainParser\\Contract\\LoggableWorkerInterface`\n\n#### Example of use\n\n```php\n\u003c?php\n\n// MyPlugin.php\n\nnamespace App;\n\nuse Szczyglis\\ChainParser\\Contract\\PluginInterface;\nuse Szczyglis\\ChainParser\\Contract\\LoggableInterface;\nuse Szczyglis\\ChainParser\\Helper\\AbstractPlugin;\n\nclass MyPlugin extends AbstractPlugin implements PluginInterface, LoggableInterface\n{\n  const NAME = 'my_plugin';\n\n  public function run(): bool\n  {\n    $this-\u003elog('some message');\n\n    return true;\n  }\n}\n\n```\n```php\n\u003c?php\n\n// MyWorker.php\n\nnamespace App;\n\nuse Szczyglis\\ChainParser\\Contract\\WorkerInterface;\nuse Szczyglis\\ChainParser\\Contract\\LoggableWorkerInterface;\nuse Szczyglis\\ChainParser\\Helper\\AbstractWorker;\n\nclass MyWorker extends AbstractWorker implements WorkerInterface, LoggableWorkerInterface\n{\n  public function doSomeJob()\n  {\n    $this-\u003elog('some message');\n  }\n}\n\n```\n___\n\n## Renderer\n\nRenderers are responsible for displaying the output. There are a few base renderers included in the package, but you can easily create your own.\n\nRenderers are located in the `Szczyglis\\ChainParser\\Renderer` namespace.\n\n### Included renderers\n\n- **TextRenderer** - parses output into TXT/HTML format\n\n- **ConsoleRenderer** - sends output to the console\n\n### How to create your own renderer\n\n- implement the interface `Szczyglis\\ChainParser\\Contract\\RendererInterface`\n- optionally extend the abstract helper class `Szczyglis\\ChainParser\\Helper\\AbstractRenderer`\n- set the renderer using `setRenderer()` method\n\n\n### Example\n\n```php  \n\u003c?php\n\n// MyRenderer.php\n\nnamespace App;\n\nuse Szczyglis\\ChainParser\\Contract\\RendererInterface;\nuse Szczyglis\\ChainParser\\Helper\\AbstractRenderer;\n\nclass MyRenderer extends AbstractRenderer implements RendererInterface\n{\n  public function renderOutput(?array $options = [])\n  {\n    foreach ($this-\u003eoutput as $item) {\n      dump($item-\u003eget('output'));\n    }\n  }\n\n  public function renderData(?array $options = [])\n  {\n    foreach ($this-\u003eoutput as $item) {\n      dump($item-\u003eget('data'));\n    }\n  }\n\n  public function renderLog(?array $options = [])\n  {\n    foreach ($this-\u003eoutput as $item) {\n      $loggers = $item-\u003egetLog();\n      foreach ($loggers as $lines) {\n        foreach ($lines as $line) {\n          dump($line);\n        }       \n      }\n    }\n  }\n} \n```\n```php\n\u003c?php\n\n// app.php\n\nuse Szczyglis\\ChainParser\\ChainParser;\nuse App\\MyRenderer;\n\n$parser = new ChainParser();\n$parser-\u003epreventDefault(); // unregister default renderer\n$parser-\u003esetRenderer(new MyRenderer()); // set your own\n\n// rest of initialization, config, etc...\n\n$parser-\u003erun();\n$parser-\u003erenderOutput(); // will display dumped output from your renderer\n$parser-\u003erenderData(); // will display dumped output from your renderer\n$parser-\u003erenderLog(); // will display dumped output from your renderer\n\n```\n\n\n## Exporting configuration\n\nYou can export the currently running configuration at any time, including the entire chain and its options. To do this, use the config generator:\n\n```php\n\u003c?php\n\n// app.php\n\nrequire __DIR__ . '/vendor/autoload.php';\n\nuse Szczyglis\\ChainParser\\ChainParser;\nuse Szczyglis\\ChainParser\\Core\\ConfigGenerator;\n\n$parser = new ChainParser;\n\n// initialization, configuration, etc...\n\n$parser-\u003erun();\n\n$myConfig = (new ConfigGenerator())-\u003ebuild($parser, 'yaml'); // yaml | json\n\ndump($myConfig); // displays configuration in Yaml format\n\n```\n\nYou can reuse an exported and saved configuration by loading it with the configuration loader `Szczyglis\\ChainParser\\Config\\YamlConfig`.\n___\n\n## Live Demo: https://szczyglis.dev/ultimate-chain-parser\n\n![src](https://user-images.githubusercontent.com/61396542/164572766-fdb57adf-c661-447f-ae55-f4394de3d3db.png)\n\n\n# Changelog\n\n**1.0.0** - Published first release. (2022-04-22)\n\n**1.0.4** - Increased limit in demo mode, documentation fixes. (2022-04-22)\n\n**1.2.6** - Full dataset sharing added, eraser and splitter plugins removed (their role is taken over by limiter), added configuration of dataset looks by freely specifying each separator for each dimension (rowset, row, column). (2022-04-23)\n\n**1.2.10** - Updated PHPDoc, updated example config YAML. (2022-04-25)\n\n**1.2.11** - Updated composer.json. (2022-04-28)\n\n**1.2.12** - Improved documentation (2024-08-26)\n\n**1.2.13** - Extended options description in docs and in the example app (2024-08-26)\n\n--- \n**Ultimate Chain Parser is free to use, but if you like it, you can support my work by buying me a coffee ;)**\n\nhttps://www.buymeacoffee.com/szczyglis\n\n**Enjoy!**\n\nMIT License | 2022 Marcin 'szczyglis' Szczygliński\n\nhttps://github.com/szczyglis-dev/ultimate-chain-parser\n\nhttps://szczyglis.dev/ultimate-chain-parser\n\nContact: szczyglis@protonmail.com\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fszczyglis-dev%2Fultimate-chain-parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fszczyglis-dev%2Fultimate-chain-parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fszczyglis-dev%2Fultimate-chain-parser/lists"}