{"id":21823527,"url":"https://github.com/jbzoo/cli","last_synced_at":"2025-04-14T04:30:58.536Z","repository":{"id":38186481,"uuid":"423189066","full_name":"JBZoo/Cli","owner":"JBZoo","description":"The framework helps create complex CLI apps and provides new tools for Symfony/Console, Symfony/Process.","archived":false,"fork":false,"pushed_at":"2025-02-02T20:24:08.000Z","size":3718,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-22T19:49:23.967Z","etag":null,"topics":["cli","command-line","console","console-app","console-application","elasticsearch","elk","jbzoo","logs","logstash","php","symfony","terminal"],"latest_commit_sha":null,"homepage":"","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/JBZoo.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}},"created_at":"2021-10-31T15:42:13.000Z","updated_at":"2025-03-21T10:14:47.000Z","dependencies_parsed_at":"2024-03-28T20:34:57.667Z","dependency_job_id":"c4aec865-89d8-40f5-a0a4-8abdacbd93d8","html_url":"https://github.com/JBZoo/Cli","commit_stats":{"total_commits":83,"total_committers":3,"mean_commits":"27.666666666666668","dds":0.1325301204819277,"last_synced_commit":"252b88b2c7afc1a8f5acea6b0957aca99a22ebbb"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBZoo%2FCli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBZoo%2FCli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBZoo%2FCli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBZoo%2FCli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JBZoo","download_url":"https://codeload.github.com/JBZoo/Cli/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248821677,"owners_count":21166926,"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":["cli","command-line","console","console-app","console-application","elasticsearch","elk","jbzoo","logs","logstash","php","symfony","terminal"],"created_at":"2024-11-27T17:33:15.383Z","updated_at":"2025-04-14T04:30:58.516Z","avatar_url":"https://github.com/JBZoo.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JBZoo / Cli\n\n[![CI](https://github.com/JBZoo/Cli/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/JBZoo/Cli/actions/workflows/main.yml?query=branch%3Amaster)    [![Coverage Status](https://coveralls.io/repos/github/JBZoo/Cli/badge.svg?branch=master)](https://coveralls.io/github/JBZoo/Cli?branch=master)    [![Psalm Coverage](https://shepherd.dev/github/JBZoo/Cli/coverage.svg)](https://shepherd.dev/github/JBZoo/Cli)    [![Psalm Level](https://shepherd.dev/github/JBZoo/Cli/level.svg)](https://shepherd.dev/github/JBZoo/Cli)    [![CodeFactor](https://www.codefactor.io/repository/github/jbzoo/cli/badge)](https://www.codefactor.io/repository/github/jbzoo/cli/issues)    \n[![Stable Version](https://poser.pugx.org/jbzoo/cli/version)](https://packagist.org/packages/jbzoo/cli/)    [![Total Downloads](https://poser.pugx.org/jbzoo/cli/downloads)](https://packagist.org/packages/jbzoo/cli/stats)    [![Dependents](https://poser.pugx.org/jbzoo/cli/dependents)](https://packagist.org/packages/jbzoo/cli/dependents?order_by=downloads)    [![GitHub License](https://img.shields.io/github/license/jbzoo/cli)](https://github.com/JBZoo/Cli/blob/master/LICENSE)\n\n\n\u003c!--ts--\u003e\n   * [Why?](#why)\n   * [Live Demo](#live-demo)\n      * [Output regular messages](#output-regular-messages)\n      * [Progress Bar Demo](#progress-bar-demo)\n   * [Quck Start - Build your first CLI App](#quck-start---build-your-first-cli-app)\n      * [Installing](#installing)\n      * [File Structure](#file-structure)\n      * [Composer file](#composer-file)\n      * [Binary file](#binary-file)\n      * [Simple CLI Action](#simple-cli-action)\n   * [Built-in Functionality](#built-in-functionality)\n      * [Sanitize input variables](#sanitize-input-variables)\n      * [Rendering text in different colors and styles](#rendering-text-in-different-colors-and-styles)\n      * [Verbosity Levels](#verbosity-levels)\n      * [Memory and time profiling](#memory-and-time-profiling)\n   * [Progress Bar](#progress-bar)\n      * [Simple example](#simple-example)\n      * [Advanced usage](#advanced-usage)\n   * [Helper Functions](#helper-functions)\n      * [Regualar question](#regualar-question)\n      * [Ask user's password](#ask-users-password)\n      * [Ask user to select the option](#ask-user-to-select-the-option)\n      * [Represent a yes/no question](#represent-a-yesno-question)\n      * [Rendering key=\u0026gt;value list](#rendering-keyvalue-list)\n   * [Easy logging](#easy-logging)\n      * [Simple log](#simple-log)\n      * [Crontab](#crontab)\n      * [Elatcisearch / Logstash (ELK)](#elatcisearch--logstash-elk)\n   * [Multi processing](#multi-processing)\n   * [Tips \u0026amp; Tricks](#tips--tricks)\n   * [Contributing](#contributing)\n   * [Useful projects and links](#useful-projects-and-links)\n   * [License](#license)\n   * [See Also](#see-also)\n\u003c!--te--\u003e\n\n## Why?\n\nThe library greatly extends the functionality of CLI App and helps make creating new console utilities in PHP quicker and easier. Here's a summary of why this library is essential:\n\n * **Enhanced Functionality**\n   * The library supercharges [Symfony/Console](https://symfony.com/doc/current/components/console.html), facilitating a more streamlined development of console utilities. \n\n * **Progress Bar Improvements**\n   * Developers gain a refined progress bar suited for loop actions and enhanced with debugging information. This makes tracking task progress and diagnosing issues a breeze. See [DemoProgressBar.php](demo/Commands/DemoProgressBar.php) and see [Live Demo](https://asciinema.org/a/601633?autoplay=1\u0026startAt=4).\n   * `$this-\u003e_($messages, $level, $context)` as part of CliCommand instead of Symfony/Console `$output-\u003ewriteln()`.\n   * `cli($messages, $level, $context)` as alias for different classes.\n   * `$this-\u003eprogressBar(iterable|int $listOrMax, \\Closure $callback, string $title = '')` as part of CliCommand instead of Symfony/Console ProgressBar.\n\n * **Strict Type Conversion**\n   * One notable feature allows for the strict conversion of option values, ensuring data integrity and reducing runtime errors. See [DemoOptionsStrictTypes.php](demo/Commands/DemoOptionsStrictTypes.php).\n   * Built-in validations for list of values. See [Sanitize input variables](#sanitize-input-variables).\n\n * **Styling and Output Customization**\n   * With built-in styles and color schemes, developers can make their console outputs more readable and visually appealing. See [DemoStyles.php](demo/Commands/DemoStyles.php).\n\n * **Message Aliases**\n   * The library introduces powerful aliases for message outputs, allowing for concise and consistent command calls. This is especially helpful in maintaining clean code.\n\n * **Advanced Options**\n   * Features such as profiling for performance, timestamping, error muting, and specialized output modes (like cron and logstash modes) empower developers to refine their console outputs and diagnostics according to their specific needs.\n   * Display timing and memory usage information with `--profile` option.\n   * Show timestamp at the beginning of each message with `--timestamp` option.\n   * Mute any sort of errors. So exit code will be always `0` (if it's possible) with `--mute-errors`.\n   * None-zero exit code on any StdErr message with `--non-zero-on-error` option.\n   * For any errors messages application will use StdOut instead of StdErr `--stdout-only` option (It's on your own risk!).\n   * Disable progress bar animation for logs with `--no-progress` option.\n\n * **Versatile Output Modes** \n   * The library provides different output formats catering to various use cases. Whether you're focusing on user-friendly text, logs, or integration with tools like ELK Stack, there's an output mode tailored for you.\n   * `--output-mode=text`. By default, text output format. Userfriendly and easy to read.\n   * `--output-mode=cron`. It's basically focused on logs output. It's combination of `--timestamp --profile --stdout-only --no-progress -vv --no-ansi`.\n   * `--output-mode=logstash`. It's basically focused on Logstash format for ELK Stack. Also, it means `--stdout-only --no-progress -vv`.\n\n * **Bonuses**\n   * There is a [multiprocess](#multi-processing) mode (please don't confuse it with multithreading) to speed up work with a monotonous dataset.\n   * Helper functions for [user input](#helper-functions) in interactive mode.\n\n\n\n## Live Demo\n\n### Output regular messages\n\n[![asciicast](https://asciinema.org/a/601633.svg)](https://asciinema.org/a/601633?autoplay=1\u0026startAt=4)\n\n\n\n### Progress Bar Demo\n\n[![asciicast](https://asciinema.org/a/601621.svg)](https://asciinema.org/a/601621?autoplay=1\u0026startAt=2)\n\n\n\n## Quck Start - Build your first CLI App\n\n### Installing\n\n```sh\ncomposer require jbzoo/cli\n```\n\n\nThe simplest CLI application has the following file structure. See the [Demo App](demo) for more details.\n\n\n### File Structure\n```\n/path/to/app/\n    my-app                      # Binrary file (See below)\n    composer.json               # Composer file\n    /Commands/                  # Commands directory\n        Simple.php              # One of the commands (See below)\n    /vendor/\n        autoload.php            # Composer autoload\n```\n\n\n### Composer file\n\n[./demo/composer.json](demo/composer.json)\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee Details\u003c/summary\u003e\n\n  ```json\n  {\n      \"name\"        : \"vendor/my-app\",\n      \"type\"        : \"project\",\n      \"description\" : \"Example of CLI App based on JBZoo/CLI\",\n      \"license\"     : \"MIT\",\n      \"keywords\"    : [\"cli\", \"application\", \"example\"],\n  \n      \"require\"     : {\n          \"php\"       : \"\u003e=8.1\",\n          \"jbzoo/cli\" : \"^7.1.0\"\n      },\n  \n      \"autoload\"    : {\n          \"psr-4\" : {\"DemoApp\\\\\" : \"\"}\n      },\n  \n      \"bin\"         : [\"my-app\"]\n  }\n  ```\n\n\u003c/details\u003e\n\n\n### Binary file\n\nBinary file: [demo/my-app](demo/my-app)\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee Details\u003c/summary\u003e\n\n  ```php\n  #!/usr/bin/env php\n\u003c?php declare(strict_types=1);\n\nnamespace DemoApp;\n\nuse JBZoo\\Cli\\CliApplication;\n\n// Init composer autoloader\nrequire_once __DIR__ . '/vendor/autoload.php';\n\n// Optional. Set your application name and version.\n$application = new CliApplication('My Console Application', 'v1.0.0');\n\n// Optional. Looks at the online generator of ASCII logos\n// https://patorjk.com/software/taag/#p=testall\u0026f=Epic\u0026t=My%20Console%20App\n$application-\u003esetLogo(\n    \u003c\u003c\u003c'EOF'\n          __  __          _____                      _\n         |  \\/  |        / ____|                    | |          /\\\n         | \\  / |_   _  | |     ___  _ __  ___  ___ | | ___     /  \\   _ __  _ __\n         | |\\/| | | | | | |    / _ \\| '_ \\/ __|/ _ \\| |/ _ \\   / /\\ \\ | '_ \\| '_ \\\n         | |  | | |_| | | |___| (_) | | | \\__ \\ (_) | |  __/  / ____ \\| |_) | |_) |\n         |_|  |_|\\__, |  \\_____\\___/|_| |_|___/\\___/|_|\\___| /_/    \\_\\ .__/| .__/\n                  __/ |                                               | |   | |\n                 |___/                                                |_|   |_|\n        EOF,\n);\n\n// Scan directory to find commands.\n//  * It doesn't work recursively!\n//  * They must be inherited from the class \\JBZoo\\Cli\\CliCommand\n$application-\u003eregisterCommandsByPath(__DIR__ . '/Commands', __NAMESPACE__);\n\n// Optional. Action name by default (if there is no arguments)\n$application-\u003esetDefaultCommand('list');\n\n// Run application\n$application-\u003erun();\n  \n  ```\n\n\u003c/details\u003e\n\n\n### Simple CLI Action\n\nThe simplest CLI action: [./demo/Commands/DemoSimple.php](demo/Commands/DemoSimple.php)\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee Details\u003c/summary\u003e\n\n  ```php\n  \u003c?php declare(strict_types=1);\n  \n  namespace DemoApp\\Commands;\n  \n  use JBZoo\\Cli\\CliCommand;\n  use JBZoo\\Cli\\Codes;\n  \n  class Simple extends CliCommand\n  {\n      protected function configure(): void\n      {\n          // Action name. It will be used in command line.\n          // Example: `./my-app simple`\n          $this-\u003esetName('simple');\n  \n          // Defined inhereted CLI options. See ./src/CliCommand.php for details.\n          parent::configure();\n      }\n  \n      protected function executeAction(): int\n      {\n          // Your code here\n          $this-\u003e_('Hello world!');\n  \n          // Exit code. 0 - success, 1 - error.\n          return self::SUCCESS;\n      }\n  }\n  ```\n\n\u003c/details\u003e\n\n\n\n## Built-in Functionality\n\n### Sanitize input variables\n\nAs live-demo take a look at demo application - [./demo/Commands/DemoOptionsStrictTypes.php](demo/Commands/DemoOptionsStrictTypes.php).\n\nTry to launch `./my-app options-strict-types`.\n\n```php\n// If the option has `InputOption::VALUE_NONE` it returns true/false.\n// --option-name\n$value = $this-\u003egetOpt('option-name'); // `$value === true` \n\n// --option-name=\"    123.6   \"\n$value = $this-\u003egetOpt('option-name'); // Returns the value AS-IS. `$value ===  \"   123.6   \"`\n\n// --option-name=\"    123.6   \"\n$value = $this-\u003egetOptBool('option-name'); // Converts an input variable to boolean. `$value === true`\n\n// --option-name=\"    123.6   \"\n$value = $this-\u003egetOptInt('option-name'); // Converts an input variable to integer. `$value === 123`\n$value = $this-\u003egetOptInt('option-name', 42, [1, 2, 42]); // Strict comparing with allowed values\n\n// --option-name=\"    123.6   \"\n$value = $this-\u003egetOptFloat('option-name'); // Converts an input variable to float. `$value === 123.6`\n$value = $this-\u003egetOptFloat('option-name', 1.0, [1.0, 2.0, 3.0]); // Strict comparing with allowed values\n\n// --option-name=\"    123.6   \"\n$value = $this-\u003egetOptString('option-name'); // Converts an input variable to trimmed string. `$value === \"123.6\"`\n$value = $this-\u003egetOptString('option-name', 'default', ['default', 'mini', 'full']); // Strict comparing with allowed values\n\n// --option-name=123.6\n$value = $this-\u003egetOptArray('option-name'); // Converts an input variable to trimmed string. `$value === [\"123.6\"]`\n\n// --option-name=\"15 July 2021 13:48:00\"\n$value = $this-\u003egetOptDatetime('option-name'); // Converts an input variable to \\DateTimeImmutable object.\n\n// Use standard input as input variable.\n// Example. `echo \" Qwerty 123 \" | php ./my-app agruments`\n$value = self::getStdIn(); // Reads StdIn as string value. `$value === \" Qwerty 123 \\n\"`\n```\n\n\n\n### Rendering text in different colors and styles\n\n\u003cimg alt=\"output-styles\" src=\".github/assets/output-styles.gif\" width=\"600\"/\u003e\n\nThere are list of predefined colors\n\n```html\n\u003cblack\u003e  Text in Black color  \u003c/black\u003e\n\u003cred\u003e    Text in Red Color    \u003c/red\u003e\n\u003cgreen\u003e  Text in Green Color  \u003c/green\u003e\n\u003cyellow\u003e Text in Yellow Color \u003c/yellow\u003e\n\u003cblue\u003e   Text in Blue Color   \u003c/blue\u003e\n\u003cmagenta\u003eText in Magenta Color\u003c/magenta\u003e\n\u003ccyan\u003e   Text in Cyan Color   \u003c/cyan\u003e\n\u003cwhite\u003e  Text in White Color  \u003c/white\u003e\n\n\u003c!-- Usually default color is white. It depends on terminal settings. --\u003e\n\u003c!-- You should use it only to overwrite nested tags. --\u003e\n\u003cdefault\u003eText in Default Color\u003c/default\u003e\n```\n\nThere are list of predefined styles\n\n```html\n\u003cbl\u003eBlinked Text\u003c/bl\u003e\n\u003cb\u003eBold Text\u003c/b\u003e\n\u003cu\u003eUnderlined Text\u003c/u\u003e\n\u003cr\u003eReverse Color/Backgroud\u003c/r\u003e\n\u003cbg\u003eChange Background Only\u003c/bg\u003e\n```\n\nAlso, you can combine colors ans styles.\n\n```html\n\u003cmagenta-bl\u003eBlinked text in magenta color\u003c/magenta-bl\u003e\n\u003cmagenta-b\u003eBold text in magenta color\u003c/magenta-b\u003e\n\u003cmagenta-u\u003eUnderlined text in magenta color\u003c/magenta-u\u003e\n\u003cmagenta-r\u003eReverse text in magenta color\u003c/magenta-r\u003e\n\u003cmagenta-bg\u003eReverse only background of text in magenta color\u003c/magenta-bg\u003e\n```\n\nAnd predefined shortcuts for standard styles of Symfony Console\n\n```html\n\u003ci\u003e alias for \u003cinfo\u003e\n\u003cc\u003e alias for \u003ccommnet\u003e\n\u003cq\u003e alias for \u003cquestion\u003e\n\u003ce\u003e alias for \u003cerror\u003e\n```\n\n\n\n### Verbosity Levels\n\nConsole commands have different verbosity levels, which determine the messages displayed in their output. \n\nAs live-demo take a look at demo application - [./demo/Commands/ExamplesOutput.php](demo/Commands/DemoOutput.php). You can see [Demo video](https://asciinema.org/a/486674).\n\nExample of usage of verbosity levels\n\n![output-full-example](.github/assets/output-full-example.png)\n\n```php\n// There two strictly(!) recommended output ways:\n\n/**\n * Prints a message to the output in the command class which inherits from the class \\JBZoo\\Cli\\CliCommand\n * \n * @param string|string[] $messages     Output message(s). Can be an array of strings or a string. Array of strings will be imploded with new line.\n * @param string          $verboseLevel is one of value form the class \\JBZoo\\Cli\\OutLvl::*\n * @param string          $context      is array of extra info. Will be serialized to JSON and displayed in the end of the message.\n */\n$this-\u003e_($messages, $verboseLevel, $context);\n\n/**\n * This is global alias function of `$this-\u003e_(...)`.\n * It's nice to have it if you want to display a text from not CliCommand class.\n */\nJBZoo\\Cli\\cli($messages, $verboseLevel, $context);\n \n```\n\n```bash\n# Do not output any message\n./my-app output -q\n./my-app output --quiet\n\n# Normal behavior, no option required. Only the most useful messages.\n./my-app output \n\n# Increase verbosity of messages\n./my-app output -v\n\n# Display also the informative non essential messages\n./my-app output -vv\n\n# Display all messages (useful to debug errors)\n./my-app output -vvv\n```\n\n\n\n### Memory and time profiling\n\nAs live-demo take a look at demo application - [./demo/Commands/DemoProfile.php](demo/Commands/DemoProfile.php).\n\nTry to launch `./my-app profile --profile`.\n\n![profiling](.github/assets/profiling.png)\n\n\n\n## Progress Bar\n\nAs live-demo take a look at demo application - [./demo/Commands/DemoProgressBar.php](demo/Commands/DemoProgressBar.php) and [Live Demo](https://asciinema.org/a/601633?autoplay=1\u0026startAt=4).\n\nYou can consider this as a substitute for the long cycles you want to profile.\n\nKeep in mind that there is an additional overhead for memory and runtime to calculate all the extra debugging information in `--verbose` mode.\n\n\n\n### Simple example\n\n![progress-default-example](.github/assets/progress-default-example.gif)\n\n```php\n$this-\u003eprogressBar(5, function (): void {\n    // Some code in loop\n});\n```\n\n\n### Advanced usage\n\n![progress-full-example](.github/assets/progress-full-example.gif)\n\n```php\n$this-\u003eprogressBar($arrayOfSomething, function ($value, $key, $step) {\n    // Some code in loop\n\n    if ($step === 3) {\n        throw new ExceptionBreak(\"Something went wrong with \\$value={$value}. Stop the loop!\");\n    }\n\n    return \"\u003cc\u003eCallback Args\u003c/c\u003e \\$value=\u003ci\u003e{$value}\u003c/i\u003e, \\$key=\u003ci\u003e{$key}\u003c/i\u003e, \\$step=\u003ci\u003e{$step}\u003c/i\u003e\";\n}, 'Custom messages based on callback arguments', $throwBatchException);\n```\n\n\n## Helper Functions\n\nAs live-demo take a look at demo application - [./demo/Commands/DemoHelpers.php](demo/Commands/DemoHelpers.php).\n\nTry to launch `./my-app helpers`.\n\nJBZoo/Cli uses [Symfony Question Helper](https://symfony.com/doc/current/components/console/helpers/questionhelper.html) as base for aliases.\n\n![helpers](.github/assets/helpers.gif)\n\n### Regualar question\n\nAsk any custom question and wait for a user's input. There is an option to set a default value.\n\n```php\n$yourName = $this-\u003eask(\"What's your name?\", 'Default Noname');\n$this-\u003e_(\"Your name is \\\"{$yourName}\\\"\");\n```\n\n### Ask user's password\n\nAsk a question and hide the response. This is particularly convenient for passwords.\nThere is an option to set a random value as default value.\n\n```php\n$yourSecret = $this-\u003easkPassword(\"New password?\", true);\n$this-\u003e_(\"Your secret is \\\"{$yourSecret}\\\"\");\n```\n\n### Ask user to select the option\n\nIf you have a predefined set of answers the user can choose from, you could use a method `askOption` which makes sure\nthat the user can only enter a valid string from a predefined list.\nThere is an option to set a default option (index or string).\n\n```php\n$selectedColor = $this-\u003easkOption(\"What's your favorite color?\", ['Red', 'Blue', 'Yellow'], 'Blue');\n$this-\u003e_(\"Selected color is {$selectedColor}\");\n```\n\n### Represent a yes/no question\n\nSuppose you want to confirm an action before actually executing it. Add the following to your command.\n\n```php\n$isConfirmed = $this-\u003econfirmation('Are you ready to execute the script?');\n$this-\u003e_(\"Is confirmed: \" . ($isConfirmed ? 'Yes' : 'No'));\n```\n\n### Rendering key=\u003evalue list\n\nIf you need to show an aligned list, use the following code.\n\n```php\nuse JBZoo\\Cli\\CliRender;\n\n$this-\u003e_(CliRender::list([\n    \"It's like a title\",\n    'Option Name' =\u003e 'Option Value',\n    'Key' =\u003e 'Value',\n    'Another Key #2' =\u003e 'Qwerty',\n], '*')); // It's bullet character\n``` \n\n```text\n * It's like a title\n * Option Name   : Option Value\n * Key           : Value\n * Another Key #2: Qwerty\n```\n\n\n## Easy logging\n\n### Simple log\n\n```bash\n./my-app output --timestamp \u003e\u003e /path/to/crontab/logs/$(date +%Y-%m-%d).log 2\u003e\u00261\n```\n\n![logs-simple](.github/assets/logs-simple.png)\n\n\n\n### Crontab\n\nJust add the `--output-mode=cron` flag and save the output to a file. Especially, this is very handy for saving logs for Crontab.\n\n```bash\n./my-app output --output-mode=cron \u003e\u003e /path/to/crontab/logs/$(date +%Y-%m-%d).log 2\u003e\u00261\n```\n\n![logs-cron](.github/assets/logs-cron.png)\n\n\n\n### Elatcisearch / Logstash (ELK)\n\nJust add the `--output-mode=logstash` flag and save the output to a file. Especially, this is very handy for saving logs for ELK Stack.\n\n```bash\n./my-app output --output-mode=logstash \u003e\u003e /path/to/logstash/logs/$(date +%Y-%m-%d).log 2\u003e\u00261\n```\n\n![logs-logstash-exception](.github/assets/logs-logstash-exception.png)\n\n\n## Multi processing\n\nThere is a multiprocess mode (please don't confuse it with multithreading) to speed up work with a monotonous dataset. Basically, `JBZoo\\Cli` will start a separate child process (not a thread!) for each dataset and wait for all of them to execute (like a Promise). This is how you get acceleration, which will depend on the power of your server and the data processing algorithm.\n\nYou will see a simple progress bar, but you won't be able to profile and log nicely, as it works for normal mode.\n\nYou can find examples here\n * [./tests/TestApp/Commands/TestSleepMulti.php](tests/TestApp/Commands/TestSleepMulti.php) - Parent command\n * [./tests/TestApp/Commands/TestSleep.php](tests/TestApp/Commands/TestSleep.php) - Child command\n\n\nNotes:\n * Pay attention on the method `executeOneProcess()` and `getListOfChildIds()` which are used to manage child processes. They are inherited from `CliCommandMultiProc` class.\n * Optimal number of child processes is `Number of CPU cores - 1` . You can override this value by setting cli options. See them here [./src/CliCommandMultiProc.php](src/CliCommandMultiProc.php).\n * Be really careful with concurrency. It's not easy to debug. Try to use `-vvv` option to see all errors and warnings.\n\n\n## Tips \u0026 Tricks\n\n * Use class `\\JBZoo\\Cli\\Codes` to get all available exit codes.\n * You can add extra context to any message. It will be serialized to JSON and displayed in the end of the message. Just use `CliHelper::getInstance()-\u003eappendExtraContext(['section' =\u003e ['var' =\u003e 'value']]);`\n * You can define constant `\\JBZOO_CLI_TIMESTAMP_REAL=true` to add `timestamp_real` as exta context. Sometimes it's useful for logstash if default value `@timestamp` doesn't work for you.\n\n\n## Contributing\n\n```shell\n# Fork the repo and build project\nmake update\n\n# Make your local changes\n\n# Run all tests and check code style\nmake test-all\n\n# Create your pull request and check all tests on GithubActions page\n```\n\n\n\n## Useful projects and links\n\n* [Symfony/Console Docs](https://symfony.com/doc/current/components/console.html)\n* [kevinlebrun/colors.php - New colors for the terminal](https://github.com/kevinlebrun/colors.php)\n* [php-school/cli-menu - Interactive menu with nested items](https://packagist.org/packages/php-school/cli-menu)\n* [nunomaduro/collision - Beautiful error reporting](https://github.com/nunomaduro/collision)\n* [splitbrain/php-cli - Lightweight and no dependencies CLI framework](https://packagist.org/packages/splitbrain/php-cli)\n* [thephpleague/climate - Allows you to easily output colored text, special formats](https://github.com/thephpleague/climate)\n* [Exit Codes With Special Meanings](https://tldp.org/LDP/abs/html/exitcodes.html)\n* [How to redirect standard (stderr) error in bash](https://www.cyberciti.biz/faq/how-to-redirect-standard-error-in-bash/)\n\n\n\n## License\n\nMIT\n\n\n\n## See Also\n\n- [CI-Report-Converter](https://github.com/JBZoo/CI-Report-Converter) - Converting different error reports for deep compatibility with popular CI systems.\n- [Composer-Diff](https://github.com/JBZoo/Composer-Diff) - See what packages have changed after `composer update`.\n- [Composer-Graph](https://github.com/JBZoo/Composer-Graph) - Dependency graph visualization of composer.json based on mermaid-js.\n- [Mermaid-PHP](https://github.com/JBZoo/Mermaid-PHP) - Generate diagrams and flowcharts with the help of the mermaid script language.\n- [Utils](https://github.com/JBZoo/Utils) - Collection of useful PHP functions, mini-classes, and snippets for every day.\n- [Image](https://github.com/JBZoo/Image) - Package provides object-oriented way to manipulate with images as simple as possible.\n- [Data](https://github.com/JBZoo/Data) - Extended implementation of ArrayObject. Use files as config/array. \n- [Retry](https://github.com/JBZoo/Retry) - Tiny PHP library providing retry/backoff functionality with multiple backoff strategies and jitter support.\n- [SimpleTypes](https://github.com/JBZoo/SimpleTypes) - Converting any values and measures - money, weight, exchange rates, length, ...\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjbzoo%2Fcli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjbzoo%2Fcli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjbzoo%2Fcli/lists"}