├── .editorconfig ├── .gitignore ├── README.md ├── composer.json ├── composer.lock └── src ├── Args ├── ArgcArgv.php ├── Getopt.php ├── Greeter.php └── README.md ├── CLImate ├── Bender.php └── README.md ├── EnvVars ├── EnvExample.php └── README.md ├── ExitCodes ├── ExitCodeExample.php └── README.md ├── PhpCliTools ├── Counter.php └── README.md ├── SymfonyConsole ├── App.php ├── HelloCommand.php ├── README.md └── ShowOffCommand.php ├── SystemCommands ├── Counter.php ├── ExecExample.php ├── PassthruExample.php ├── README.md ├── ShellExecExample.php └── SystemExample.php └── WPCLIExample.php /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | indent_size = 4 10 | indent_style = space 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | vendor 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP CLI Examples 2 | 3 | This repository contains several sample PHP CLI scripts to accompany my talk, [_Building for the PHP Command Line Interface_](https://github.com/stevegrunwell/building-for-php-cli). 4 | 5 | Within the `src/` directory, you'll find sub-directories covering the following topics: 6 | 7 | * [Argument handling](src/Args) 8 | * [Environment variables](src/EnvVars) 9 | * [Exit codes](src/ExitCodes) 10 | * [System commands](src/SystemCommands) 11 | 12 | There are also demonstrations of the tools discussed: 13 | 14 | * [Symfony Console](src/SymfonyConsole) 15 | * [PHP-CLI Tools](src/PhpCliTools) 16 | * [CLImate](src/CLImate) 17 | 18 | ## Installation 19 | 20 | To install these samples locally, please start by installing their dependencies via [Composer](https://getcomposer.org/): 21 | 22 | ```sh 23 | composer install 24 | ``` 25 | 26 | ## License 27 | 28 | The MIT License (MIT) 29 | Copyright (c) 2024 Steve Grunwell 30 | 31 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 32 | 33 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 34 | 35 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stevegrunwell/php-cli-examples", 3 | "description": "Example PHP CLI scripts for various platforms.", 4 | "license": "MIT", 5 | "homepage": "https://github.com/stevegrunwell/php-cli-examples", 6 | "authors": [ 7 | { 8 | "name": "Steve Grunwell", 9 | "homepage": "https://stevegrunwell.com" 10 | } 11 | ], 12 | "require": { 13 | "php": "^8.0", 14 | "league/climate": "^3.8", 15 | "symfony/console": "^7.0", 16 | "wp-cli/php-cli-tools": "^0.11" 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "SteveGrunwell\\PhpCliExamples\\": "src/" 21 | } 22 | }, 23 | "config": { 24 | "sort-packages": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "15789c17e83d67f5fc2dac06fad727ac", 8 | "packages": [ 9 | { 10 | "name": "league/climate", 11 | "version": "3.8.2", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/thephpleague/climate.git", 15 | "reference": "a785a3ac8f584eed4abd45e4e16fe64c46659a28" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/thephpleague/climate/zipball/a785a3ac8f584eed4abd45e4e16fe64c46659a28", 20 | "reference": "a785a3ac8f584eed4abd45e4e16fe64c46659a28", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": "^7.3 || ^8.0", 25 | "psr/log": "^1.0 || ^2.0 || ^3.0", 26 | "seld/cli-prompt": "^1.0" 27 | }, 28 | "require-dev": { 29 | "mikey179/vfsstream": "^1.6.10", 30 | "mockery/mockery": "^1.4.2", 31 | "phpunit/phpunit": "^9.5.10" 32 | }, 33 | "suggest": { 34 | "ext-mbstring": "If ext-mbstring is not available you MUST install symfony/polyfill-mbstring" 35 | }, 36 | "type": "library", 37 | "autoload": { 38 | "psr-4": { 39 | "League\\CLImate\\": "src/" 40 | } 41 | }, 42 | "notification-url": "https://packagist.org/downloads/", 43 | "license": [ 44 | "MIT" 45 | ], 46 | "authors": [ 47 | { 48 | "name": "Joe Tannenbaum", 49 | "email": "hey@joe.codes", 50 | "homepage": "http://joe.codes/", 51 | "role": "Developer" 52 | }, 53 | { 54 | "name": "Craig Duncan", 55 | "email": "git@duncanc.co.uk", 56 | "homepage": "https://github.com/duncan3dc", 57 | "role": "Developer" 58 | } 59 | ], 60 | "description": "PHP's best friend for the terminal. CLImate allows you to easily output colored text, special formats, and more.", 61 | "keywords": [ 62 | "cli", 63 | "colors", 64 | "command", 65 | "php", 66 | "terminal" 67 | ], 68 | "support": { 69 | "issues": "https://github.com/thephpleague/climate/issues", 70 | "source": "https://github.com/thephpleague/climate/tree/3.8.2" 71 | }, 72 | "time": "2022-06-18T14:42:08+00:00" 73 | }, 74 | { 75 | "name": "psr/container", 76 | "version": "2.0.2", 77 | "source": { 78 | "type": "git", 79 | "url": "https://github.com/php-fig/container.git", 80 | "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" 81 | }, 82 | "dist": { 83 | "type": "zip", 84 | "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", 85 | "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", 86 | "shasum": "" 87 | }, 88 | "require": { 89 | "php": ">=7.4.0" 90 | }, 91 | "type": "library", 92 | "extra": { 93 | "branch-alias": { 94 | "dev-master": "2.0.x-dev" 95 | } 96 | }, 97 | "autoload": { 98 | "psr-4": { 99 | "Psr\\Container\\": "src/" 100 | } 101 | }, 102 | "notification-url": "https://packagist.org/downloads/", 103 | "license": [ 104 | "MIT" 105 | ], 106 | "authors": [ 107 | { 108 | "name": "PHP-FIG", 109 | "homepage": "https://www.php-fig.org/" 110 | } 111 | ], 112 | "description": "Common Container Interface (PHP FIG PSR-11)", 113 | "homepage": "https://github.com/php-fig/container", 114 | "keywords": [ 115 | "PSR-11", 116 | "container", 117 | "container-interface", 118 | "container-interop", 119 | "psr" 120 | ], 121 | "support": { 122 | "issues": "https://github.com/php-fig/container/issues", 123 | "source": "https://github.com/php-fig/container/tree/2.0.2" 124 | }, 125 | "time": "2021-11-05T16:47:00+00:00" 126 | }, 127 | { 128 | "name": "psr/log", 129 | "version": "3.0.0", 130 | "source": { 131 | "type": "git", 132 | "url": "https://github.com/php-fig/log.git", 133 | "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" 134 | }, 135 | "dist": { 136 | "type": "zip", 137 | "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", 138 | "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", 139 | "shasum": "" 140 | }, 141 | "require": { 142 | "php": ">=8.0.0" 143 | }, 144 | "type": "library", 145 | "extra": { 146 | "branch-alias": { 147 | "dev-master": "3.x-dev" 148 | } 149 | }, 150 | "autoload": { 151 | "psr-4": { 152 | "Psr\\Log\\": "src" 153 | } 154 | }, 155 | "notification-url": "https://packagist.org/downloads/", 156 | "license": [ 157 | "MIT" 158 | ], 159 | "authors": [ 160 | { 161 | "name": "PHP-FIG", 162 | "homepage": "https://www.php-fig.org/" 163 | } 164 | ], 165 | "description": "Common interface for logging libraries", 166 | "homepage": "https://github.com/php-fig/log", 167 | "keywords": [ 168 | "log", 169 | "psr", 170 | "psr-3" 171 | ], 172 | "support": { 173 | "source": "https://github.com/php-fig/log/tree/3.0.0" 174 | }, 175 | "time": "2021-07-14T16:46:02+00:00" 176 | }, 177 | { 178 | "name": "seld/cli-prompt", 179 | "version": "1.0.4", 180 | "source": { 181 | "type": "git", 182 | "url": "https://github.com/Seldaek/cli-prompt.git", 183 | "reference": "b8dfcf02094b8c03b40322c229493bb2884423c5" 184 | }, 185 | "dist": { 186 | "type": "zip", 187 | "url": "https://api.github.com/repos/Seldaek/cli-prompt/zipball/b8dfcf02094b8c03b40322c229493bb2884423c5", 188 | "reference": "b8dfcf02094b8c03b40322c229493bb2884423c5", 189 | "shasum": "" 190 | }, 191 | "require": { 192 | "php": ">=5.3" 193 | }, 194 | "require-dev": { 195 | "phpstan/phpstan": "^0.12.63" 196 | }, 197 | "type": "library", 198 | "extra": { 199 | "branch-alias": { 200 | "dev-master": "1.x-dev" 201 | } 202 | }, 203 | "autoload": { 204 | "psr-4": { 205 | "Seld\\CliPrompt\\": "src/" 206 | } 207 | }, 208 | "notification-url": "https://packagist.org/downloads/", 209 | "license": [ 210 | "MIT" 211 | ], 212 | "authors": [ 213 | { 214 | "name": "Jordi Boggiano", 215 | "email": "j.boggiano@seld.be" 216 | } 217 | ], 218 | "description": "Allows you to prompt for user input on the command line, and optionally hide the characters they type", 219 | "keywords": [ 220 | "cli", 221 | "console", 222 | "hidden", 223 | "input", 224 | "prompt" 225 | ], 226 | "support": { 227 | "issues": "https://github.com/Seldaek/cli-prompt/issues", 228 | "source": "https://github.com/Seldaek/cli-prompt/tree/1.0.4" 229 | }, 230 | "time": "2020-12-15T21:32:01+00:00" 231 | }, 232 | { 233 | "name": "symfony/console", 234 | "version": "v7.0.6", 235 | "source": { 236 | "type": "git", 237 | "url": "https://github.com/symfony/console.git", 238 | "reference": "fde915cd8e7eb99b3d531d3d5c09531429c3f9e5" 239 | }, 240 | "dist": { 241 | "type": "zip", 242 | "url": "https://api.github.com/repos/symfony/console/zipball/fde915cd8e7eb99b3d531d3d5c09531429c3f9e5", 243 | "reference": "fde915cd8e7eb99b3d531d3d5c09531429c3f9e5", 244 | "shasum": "" 245 | }, 246 | "require": { 247 | "php": ">=8.2", 248 | "symfony/polyfill-mbstring": "~1.0", 249 | "symfony/service-contracts": "^2.5|^3", 250 | "symfony/string": "^6.4|^7.0" 251 | }, 252 | "conflict": { 253 | "symfony/dependency-injection": "<6.4", 254 | "symfony/dotenv": "<6.4", 255 | "symfony/event-dispatcher": "<6.4", 256 | "symfony/lock": "<6.4", 257 | "symfony/process": "<6.4" 258 | }, 259 | "provide": { 260 | "psr/log-implementation": "1.0|2.0|3.0" 261 | }, 262 | "require-dev": { 263 | "psr/log": "^1|^2|^3", 264 | "symfony/config": "^6.4|^7.0", 265 | "symfony/dependency-injection": "^6.4|^7.0", 266 | "symfony/event-dispatcher": "^6.4|^7.0", 267 | "symfony/http-foundation": "^6.4|^7.0", 268 | "symfony/http-kernel": "^6.4|^7.0", 269 | "symfony/lock": "^6.4|^7.0", 270 | "symfony/messenger": "^6.4|^7.0", 271 | "symfony/process": "^6.4|^7.0", 272 | "symfony/stopwatch": "^6.4|^7.0", 273 | "symfony/var-dumper": "^6.4|^7.0" 274 | }, 275 | "type": "library", 276 | "autoload": { 277 | "psr-4": { 278 | "Symfony\\Component\\Console\\": "" 279 | }, 280 | "exclude-from-classmap": [ 281 | "/Tests/" 282 | ] 283 | }, 284 | "notification-url": "https://packagist.org/downloads/", 285 | "license": [ 286 | "MIT" 287 | ], 288 | "authors": [ 289 | { 290 | "name": "Fabien Potencier", 291 | "email": "fabien@symfony.com" 292 | }, 293 | { 294 | "name": "Symfony Community", 295 | "homepage": "https://symfony.com/contributors" 296 | } 297 | ], 298 | "description": "Eases the creation of beautiful and testable command line interfaces", 299 | "homepage": "https://symfony.com", 300 | "keywords": [ 301 | "cli", 302 | "command-line", 303 | "console", 304 | "terminal" 305 | ], 306 | "support": { 307 | "source": "https://github.com/symfony/console/tree/v7.0.6" 308 | }, 309 | "funding": [ 310 | { 311 | "url": "https://symfony.com/sponsor", 312 | "type": "custom" 313 | }, 314 | { 315 | "url": "https://github.com/fabpot", 316 | "type": "github" 317 | }, 318 | { 319 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 320 | "type": "tidelift" 321 | } 322 | ], 323 | "time": "2024-04-01T11:04:53+00:00" 324 | }, 325 | { 326 | "name": "symfony/polyfill-ctype", 327 | "version": "v1.29.0", 328 | "source": { 329 | "type": "git", 330 | "url": "https://github.com/symfony/polyfill-ctype.git", 331 | "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" 332 | }, 333 | "dist": { 334 | "type": "zip", 335 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", 336 | "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", 337 | "shasum": "" 338 | }, 339 | "require": { 340 | "php": ">=7.1" 341 | }, 342 | "provide": { 343 | "ext-ctype": "*" 344 | }, 345 | "suggest": { 346 | "ext-ctype": "For best performance" 347 | }, 348 | "type": "library", 349 | "extra": { 350 | "thanks": { 351 | "name": "symfony/polyfill", 352 | "url": "https://github.com/symfony/polyfill" 353 | } 354 | }, 355 | "autoload": { 356 | "files": [ 357 | "bootstrap.php" 358 | ], 359 | "psr-4": { 360 | "Symfony\\Polyfill\\Ctype\\": "" 361 | } 362 | }, 363 | "notification-url": "https://packagist.org/downloads/", 364 | "license": [ 365 | "MIT" 366 | ], 367 | "authors": [ 368 | { 369 | "name": "Gert de Pagter", 370 | "email": "BackEndTea@gmail.com" 371 | }, 372 | { 373 | "name": "Symfony Community", 374 | "homepage": "https://symfony.com/contributors" 375 | } 376 | ], 377 | "description": "Symfony polyfill for ctype functions", 378 | "homepage": "https://symfony.com", 379 | "keywords": [ 380 | "compatibility", 381 | "ctype", 382 | "polyfill", 383 | "portable" 384 | ], 385 | "support": { 386 | "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" 387 | }, 388 | "funding": [ 389 | { 390 | "url": "https://symfony.com/sponsor", 391 | "type": "custom" 392 | }, 393 | { 394 | "url": "https://github.com/fabpot", 395 | "type": "github" 396 | }, 397 | { 398 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 399 | "type": "tidelift" 400 | } 401 | ], 402 | "time": "2024-01-29T20:11:03+00:00" 403 | }, 404 | { 405 | "name": "symfony/polyfill-intl-grapheme", 406 | "version": "v1.29.0", 407 | "source": { 408 | "type": "git", 409 | "url": "https://github.com/symfony/polyfill-intl-grapheme.git", 410 | "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" 411 | }, 412 | "dist": { 413 | "type": "zip", 414 | "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", 415 | "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", 416 | "shasum": "" 417 | }, 418 | "require": { 419 | "php": ">=7.1" 420 | }, 421 | "suggest": { 422 | "ext-intl": "For best performance" 423 | }, 424 | "type": "library", 425 | "extra": { 426 | "thanks": { 427 | "name": "symfony/polyfill", 428 | "url": "https://github.com/symfony/polyfill" 429 | } 430 | }, 431 | "autoload": { 432 | "files": [ 433 | "bootstrap.php" 434 | ], 435 | "psr-4": { 436 | "Symfony\\Polyfill\\Intl\\Grapheme\\": "" 437 | } 438 | }, 439 | "notification-url": "https://packagist.org/downloads/", 440 | "license": [ 441 | "MIT" 442 | ], 443 | "authors": [ 444 | { 445 | "name": "Nicolas Grekas", 446 | "email": "p@tchwork.com" 447 | }, 448 | { 449 | "name": "Symfony Community", 450 | "homepage": "https://symfony.com/contributors" 451 | } 452 | ], 453 | "description": "Symfony polyfill for intl's grapheme_* functions", 454 | "homepage": "https://symfony.com", 455 | "keywords": [ 456 | "compatibility", 457 | "grapheme", 458 | "intl", 459 | "polyfill", 460 | "portable", 461 | "shim" 462 | ], 463 | "support": { 464 | "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" 465 | }, 466 | "funding": [ 467 | { 468 | "url": "https://symfony.com/sponsor", 469 | "type": "custom" 470 | }, 471 | { 472 | "url": "https://github.com/fabpot", 473 | "type": "github" 474 | }, 475 | { 476 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 477 | "type": "tidelift" 478 | } 479 | ], 480 | "time": "2024-01-29T20:11:03+00:00" 481 | }, 482 | { 483 | "name": "symfony/polyfill-intl-normalizer", 484 | "version": "v1.29.0", 485 | "source": { 486 | "type": "git", 487 | "url": "https://github.com/symfony/polyfill-intl-normalizer.git", 488 | "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" 489 | }, 490 | "dist": { 491 | "type": "zip", 492 | "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", 493 | "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", 494 | "shasum": "" 495 | }, 496 | "require": { 497 | "php": ">=7.1" 498 | }, 499 | "suggest": { 500 | "ext-intl": "For best performance" 501 | }, 502 | "type": "library", 503 | "extra": { 504 | "thanks": { 505 | "name": "symfony/polyfill", 506 | "url": "https://github.com/symfony/polyfill" 507 | } 508 | }, 509 | "autoload": { 510 | "files": [ 511 | "bootstrap.php" 512 | ], 513 | "psr-4": { 514 | "Symfony\\Polyfill\\Intl\\Normalizer\\": "" 515 | }, 516 | "classmap": [ 517 | "Resources/stubs" 518 | ] 519 | }, 520 | "notification-url": "https://packagist.org/downloads/", 521 | "license": [ 522 | "MIT" 523 | ], 524 | "authors": [ 525 | { 526 | "name": "Nicolas Grekas", 527 | "email": "p@tchwork.com" 528 | }, 529 | { 530 | "name": "Symfony Community", 531 | "homepage": "https://symfony.com/contributors" 532 | } 533 | ], 534 | "description": "Symfony polyfill for intl's Normalizer class and related functions", 535 | "homepage": "https://symfony.com", 536 | "keywords": [ 537 | "compatibility", 538 | "intl", 539 | "normalizer", 540 | "polyfill", 541 | "portable", 542 | "shim" 543 | ], 544 | "support": { 545 | "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" 546 | }, 547 | "funding": [ 548 | { 549 | "url": "https://symfony.com/sponsor", 550 | "type": "custom" 551 | }, 552 | { 553 | "url": "https://github.com/fabpot", 554 | "type": "github" 555 | }, 556 | { 557 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 558 | "type": "tidelift" 559 | } 560 | ], 561 | "time": "2024-01-29T20:11:03+00:00" 562 | }, 563 | { 564 | "name": "symfony/polyfill-mbstring", 565 | "version": "v1.29.0", 566 | "source": { 567 | "type": "git", 568 | "url": "https://github.com/symfony/polyfill-mbstring.git", 569 | "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" 570 | }, 571 | "dist": { 572 | "type": "zip", 573 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", 574 | "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", 575 | "shasum": "" 576 | }, 577 | "require": { 578 | "php": ">=7.1" 579 | }, 580 | "provide": { 581 | "ext-mbstring": "*" 582 | }, 583 | "suggest": { 584 | "ext-mbstring": "For best performance" 585 | }, 586 | "type": "library", 587 | "extra": { 588 | "thanks": { 589 | "name": "symfony/polyfill", 590 | "url": "https://github.com/symfony/polyfill" 591 | } 592 | }, 593 | "autoload": { 594 | "files": [ 595 | "bootstrap.php" 596 | ], 597 | "psr-4": { 598 | "Symfony\\Polyfill\\Mbstring\\": "" 599 | } 600 | }, 601 | "notification-url": "https://packagist.org/downloads/", 602 | "license": [ 603 | "MIT" 604 | ], 605 | "authors": [ 606 | { 607 | "name": "Nicolas Grekas", 608 | "email": "p@tchwork.com" 609 | }, 610 | { 611 | "name": "Symfony Community", 612 | "homepage": "https://symfony.com/contributors" 613 | } 614 | ], 615 | "description": "Symfony polyfill for the Mbstring extension", 616 | "homepage": "https://symfony.com", 617 | "keywords": [ 618 | "compatibility", 619 | "mbstring", 620 | "polyfill", 621 | "portable", 622 | "shim" 623 | ], 624 | "support": { 625 | "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" 626 | }, 627 | "funding": [ 628 | { 629 | "url": "https://symfony.com/sponsor", 630 | "type": "custom" 631 | }, 632 | { 633 | "url": "https://github.com/fabpot", 634 | "type": "github" 635 | }, 636 | { 637 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 638 | "type": "tidelift" 639 | } 640 | ], 641 | "time": "2024-01-29T20:11:03+00:00" 642 | }, 643 | { 644 | "name": "symfony/service-contracts", 645 | "version": "v3.4.2", 646 | "source": { 647 | "type": "git", 648 | "url": "https://github.com/symfony/service-contracts.git", 649 | "reference": "11bbf19a0fb7b36345861e85c5768844c552906e" 650 | }, 651 | "dist": { 652 | "type": "zip", 653 | "url": "https://api.github.com/repos/symfony/service-contracts/zipball/11bbf19a0fb7b36345861e85c5768844c552906e", 654 | "reference": "11bbf19a0fb7b36345861e85c5768844c552906e", 655 | "shasum": "" 656 | }, 657 | "require": { 658 | "php": ">=8.1", 659 | "psr/container": "^1.1|^2.0" 660 | }, 661 | "conflict": { 662 | "ext-psr": "<1.1|>=2" 663 | }, 664 | "type": "library", 665 | "extra": { 666 | "branch-alias": { 667 | "dev-main": "3.4-dev" 668 | }, 669 | "thanks": { 670 | "name": "symfony/contracts", 671 | "url": "https://github.com/symfony/contracts" 672 | } 673 | }, 674 | "autoload": { 675 | "psr-4": { 676 | "Symfony\\Contracts\\Service\\": "" 677 | }, 678 | "exclude-from-classmap": [ 679 | "/Test/" 680 | ] 681 | }, 682 | "notification-url": "https://packagist.org/downloads/", 683 | "license": [ 684 | "MIT" 685 | ], 686 | "authors": [ 687 | { 688 | "name": "Nicolas Grekas", 689 | "email": "p@tchwork.com" 690 | }, 691 | { 692 | "name": "Symfony Community", 693 | "homepage": "https://symfony.com/contributors" 694 | } 695 | ], 696 | "description": "Generic abstractions related to writing services", 697 | "homepage": "https://symfony.com", 698 | "keywords": [ 699 | "abstractions", 700 | "contracts", 701 | "decoupling", 702 | "interfaces", 703 | "interoperability", 704 | "standards" 705 | ], 706 | "support": { 707 | "source": "https://github.com/symfony/service-contracts/tree/v3.4.2" 708 | }, 709 | "funding": [ 710 | { 711 | "url": "https://symfony.com/sponsor", 712 | "type": "custom" 713 | }, 714 | { 715 | "url": "https://github.com/fabpot", 716 | "type": "github" 717 | }, 718 | { 719 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 720 | "type": "tidelift" 721 | } 722 | ], 723 | "time": "2023-12-19T21:51:00+00:00" 724 | }, 725 | { 726 | "name": "symfony/string", 727 | "version": "v7.0.4", 728 | "source": { 729 | "type": "git", 730 | "url": "https://github.com/symfony/string.git", 731 | "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b" 732 | }, 733 | "dist": { 734 | "type": "zip", 735 | "url": "https://api.github.com/repos/symfony/string/zipball/f5832521b998b0bec40bee688ad5de98d4cf111b", 736 | "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b", 737 | "shasum": "" 738 | }, 739 | "require": { 740 | "php": ">=8.2", 741 | "symfony/polyfill-ctype": "~1.8", 742 | "symfony/polyfill-intl-grapheme": "~1.0", 743 | "symfony/polyfill-intl-normalizer": "~1.0", 744 | "symfony/polyfill-mbstring": "~1.0" 745 | }, 746 | "conflict": { 747 | "symfony/translation-contracts": "<2.5" 748 | }, 749 | "require-dev": { 750 | "symfony/error-handler": "^6.4|^7.0", 751 | "symfony/http-client": "^6.4|^7.0", 752 | "symfony/intl": "^6.4|^7.0", 753 | "symfony/translation-contracts": "^2.5|^3.0", 754 | "symfony/var-exporter": "^6.4|^7.0" 755 | }, 756 | "type": "library", 757 | "autoload": { 758 | "files": [ 759 | "Resources/functions.php" 760 | ], 761 | "psr-4": { 762 | "Symfony\\Component\\String\\": "" 763 | }, 764 | "exclude-from-classmap": [ 765 | "/Tests/" 766 | ] 767 | }, 768 | "notification-url": "https://packagist.org/downloads/", 769 | "license": [ 770 | "MIT" 771 | ], 772 | "authors": [ 773 | { 774 | "name": "Nicolas Grekas", 775 | "email": "p@tchwork.com" 776 | }, 777 | { 778 | "name": "Symfony Community", 779 | "homepage": "https://symfony.com/contributors" 780 | } 781 | ], 782 | "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", 783 | "homepage": "https://symfony.com", 784 | "keywords": [ 785 | "grapheme", 786 | "i18n", 787 | "string", 788 | "unicode", 789 | "utf-8", 790 | "utf8" 791 | ], 792 | "support": { 793 | "source": "https://github.com/symfony/string/tree/v7.0.4" 794 | }, 795 | "funding": [ 796 | { 797 | "url": "https://symfony.com/sponsor", 798 | "type": "custom" 799 | }, 800 | { 801 | "url": "https://github.com/fabpot", 802 | "type": "github" 803 | }, 804 | { 805 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 806 | "type": "tidelift" 807 | } 808 | ], 809 | "time": "2024-02-01T13:17:36+00:00" 810 | }, 811 | { 812 | "name": "wp-cli/php-cli-tools", 813 | "version": "v0.11.22", 814 | "source": { 815 | "type": "git", 816 | "url": "https://github.com/wp-cli/php-cli-tools.git", 817 | "reference": "a6bb94664ca36d0962f9c2ff25591c315a550c51" 818 | }, 819 | "dist": { 820 | "type": "zip", 821 | "url": "https://api.github.com/repos/wp-cli/php-cli-tools/zipball/a6bb94664ca36d0962f9c2ff25591c315a550c51", 822 | "reference": "a6bb94664ca36d0962f9c2ff25591c315a550c51", 823 | "shasum": "" 824 | }, 825 | "require": { 826 | "php": ">= 5.3.0" 827 | }, 828 | "require-dev": { 829 | "roave/security-advisories": "dev-latest", 830 | "wp-cli/wp-cli-tests": "^4" 831 | }, 832 | "type": "library", 833 | "extra": { 834 | "branch-alias": { 835 | "dev-master": "0.11.x-dev" 836 | } 837 | }, 838 | "autoload": { 839 | "files": [ 840 | "lib/cli/cli.php" 841 | ], 842 | "psr-0": { 843 | "cli": "lib/" 844 | } 845 | }, 846 | "notification-url": "https://packagist.org/downloads/", 847 | "license": [ 848 | "MIT" 849 | ], 850 | "authors": [ 851 | { 852 | "name": "Daniel Bachhuber", 853 | "email": "daniel@handbuilt.co", 854 | "role": "Maintainer" 855 | }, 856 | { 857 | "name": "James Logsdon", 858 | "email": "jlogsdon@php.net", 859 | "role": "Developer" 860 | } 861 | ], 862 | "description": "Console utilities for PHP", 863 | "homepage": "http://github.com/wp-cli/php-cli-tools", 864 | "keywords": [ 865 | "cli", 866 | "console" 867 | ], 868 | "support": { 869 | "issues": "https://github.com/wp-cli/php-cli-tools/issues", 870 | "source": "https://github.com/wp-cli/php-cli-tools/tree/v0.11.22" 871 | }, 872 | "time": "2023-12-03T19:25:05+00:00" 873 | } 874 | ], 875 | "packages-dev": [], 876 | "aliases": [], 877 | "minimum-stability": "stable", 878 | "stability-flags": [], 879 | "prefer-stable": false, 880 | "prefer-lowest": false, 881 | "platform": { 882 | "php": "^8.0" 883 | }, 884 | "platform-dev": [], 885 | "plugin-api-version": "2.6.0" 886 | } 887 | -------------------------------------------------------------------------------- /src/Args/ArgcArgv.php: -------------------------------------------------------------------------------- 1 | $value) { 28 | $dashes = in_array($opt, ['r', 'o', 'b']) ? '-' : '--'; 29 | 30 | printf("\t%s%s: %s\n", $dashes, $opt, var_export($value, true)); 31 | } 32 | 33 | $remaining_args = array_slice($argv, $index); 34 | 35 | if (!empty($remaining_args)) { 36 | echo "\nWith remaining argument(s):\n"; 37 | array_walk($remaining_args, fn ($arg) => printf("\t* %s\n", $arg)); 38 | } 39 | -------------------------------------------------------------------------------- /src/Args/Greeter.php: -------------------------------------------------------------------------------- 1 | examples/ArgExample.php 17 | [1] => abc 18 | [2] => 123 19 | ) 20 | ``` 21 | 22 | ## [Getopt.php](Getopt.php) 23 | 24 | This script demonstrates the behavior of PHP's `getopt()` function and accepts three short arguments and three long arguments: 25 | 26 | ```none 27 | -r|--required This option must have a value. 28 | -o|--optional This option may or may not have a value. 29 | -b|--boolean This option must not have a value. 30 | ``` 31 | 32 | Notice how `getopt()` can handle a few conventions for passing args; any of the following will produce the same results: 33 | 34 | ```sh 35 | # Spaces between the options and values 36 | $ php Getopt.php -r "-r value" --required "--required value" 37 | This is what getopt() parsed out of your call: 38 | 39 | -r: '-r value' 40 | --required: '--required value' 41 | 42 | # Equal signs between the options and values 43 | $ php Getopt.php -r="-r value" --required="--required value" 44 | This is what getopt() parsed out of your call: 45 | 46 | -r: '-r value' 47 | --required: '--required value' 48 | ``` 49 | 50 | However, the same cannot be said for **nothing** between option and value: 51 | 52 | ```sh 53 | # No spaces between the options and values 54 | php Getopt.php -r"-r value" --required"--required value" 55 | This is what getopt() parsed out of your call: 56 | 57 | -r: '-r value' 58 | ``` 59 | 60 | When dealing with optional values, spaces cannot exist between the short option and value: 61 | 62 | ```sh 63 | # Equal sign between options and values 64 | php Getopt.php -o="-o value" --optional="-optional value" 65 | This is what getopt() parsed out of your call: 66 | 67 | -o: '-o value' 68 | --optional: '-optional value' 69 | 70 | # Space between the option and value 71 | php Getopt.php -o "-o value" --optional "-optional value" 72 | This is what getopt() parsed out of your call: 73 | 74 | -o: array ( 75 | 0 => false, 76 | 1 => ' value', 77 | 2 => 'ptional value', 78 | ) 79 | --optional: false 80 | 81 | # No space between the options and values 82 | $ php Getopt.php -o"-o value" --optional"-optional value" 83 | This is what getopt() parsed out of your call: 84 | 85 | -o: '-o value' 86 | ``` 87 | 88 | When values are not passed, the value will be interpreted as `false`; the presence of the array key in the `$opts` array is how we determine if the option was passed: 89 | 90 | ```sh 91 | $ php Getopt.php -o -b --optional 92 | This is what getopt() parsed out of your call: 93 | 94 | -o: false 95 | -b: false 96 | --optional: false 97 | ``` 98 | 99 | Short options can be combined, but any option that accepts a value must be at the end of the list: 100 | `-ob` is treated as `-o=b`, not `-o -b`: 101 | 102 | ```sh 103 | # -ob is treated as -o=b 104 | $ php Getopt.php -ob 105 | This is what getopt() parsed out of your call: 106 | 107 | -o: 'b' 108 | 109 | # -bo is treated as -b -o 110 | $ php Getopt.php -bo 111 | This is what getopt() parsed out of your call: 112 | 113 | -b: false 114 | -o: false 115 | ``` 116 | 117 | Additionally, if a value is passed to an argument that does not accept a value, all subsequent options will be skipped: 118 | 119 | ```sh 120 | php Getopt.php --required value1 --boolean value2 --optional value3 121 | This is what getopt() parsed out of your call: 122 | 123 | --required: 'value1' 124 | --boolean: false 125 | 126 | Stopped parsing after 'value2' 127 | ``` 128 | -------------------------------------------------------------------------------- /src/CLImate/Bender.php: -------------------------------------------------------------------------------- 1 | cyan()->underline()->blink("Let's do something cool!")->br(); 15 | $name_prompt = $cli->input('First, what is your name?')->defaultTo('Fry'); 16 | $name = $name_prompt->prompt(); 17 | 18 | $confirmation = $cli->confirm("Okay, {$name}, are you ready?"); 19 | 20 | if (! $confirmation->confirmed()) { 21 | $cli->error('Too bad, maybe next time!'); 22 | exit; 23 | } 24 | 25 | $direction_prompt = $cli->radio( 26 | "Where's Bender?", 27 | [ 28 | 'top' => 'Up there', 29 | 'bottom' => 'Down there', 30 | 'left' => 'To my left', 31 | 'right' => 'To my right', 32 | ] 33 | ); 34 | $direction = $direction_prompt->prompt(); 35 | $cli->clear(); 36 | $cli->animation('bender')->enterFrom($direction); 37 | 38 | $cli->br()->green('This concludes our game of "Where\'s Bender?" Goodbye!'); 39 | -------------------------------------------------------------------------------- /src/CLImate/README.md: -------------------------------------------------------------------------------- 1 | # CLImate 2 | 3 | The example in this directory demonstrates some of the more fun features of [CLImate](https://climate.thephpleague.com/), but I won't ruin the surprise! 😉 4 | 5 | ```sh 6 | $ php Bender.php 7 | ``` 8 | -------------------------------------------------------------------------------- /src/EnvVars/EnvExample.php: -------------------------------------------------------------------------------- 1 | ', 53 | exec('echo $ANIMAL_SOUND') ?: '' 54 | ); 55 | -------------------------------------------------------------------------------- /src/EnvVars/README.md: -------------------------------------------------------------------------------- 1 | # Working with Environment Variables 2 | 3 | This example demonstrates how environment variables can be set and retrieved via PHP scripts via [`getenv()`](https://www.php.net/manual/en/function.getenv.php) and [`putenv()`](https://www.php.net/manual/en/function.putenv.php). 4 | 5 | ``` 6 | $ ANIMAL=dog php EnvExample.php 7 | The dog goes "woof!" 8 | 9 | ANIMAL= 10 | ANIMAL_SOUND=woof! 11 | 12 | $ ANIMAL=fox php EnvExample.php 13 | I don't know what sound the fox makes! 14 | 15 | ANIMAL=fox 16 | ANIMAL_SOUND=unknown 17 | ``` 18 | -------------------------------------------------------------------------------- /src/ExitCodes/ExitCodeExample.php: -------------------------------------------------------------------------------- 1 | 1 ? 1 : 0; 27 | 28 | printf('Exiting with a code of %d' . PHP_EOL, $code); 29 | 30 | exit($code); 31 | -------------------------------------------------------------------------------- /src/ExitCodes/README.md: -------------------------------------------------------------------------------- 1 | # Exit Codes 2 | 3 | This directory contains a script demonstrating how exit codes work. 4 | 5 | > **Note:** Technically, `exit` is a language construct, not a function, though it operates much like one. 6 | 7 | ## [ExitCodeExample.php](ExitCodeExample.php) 8 | 9 | Passing any arguments will cause the script to exit with an exit code of 1, which can be used to demonstrate boolean operators: 10 | 11 | ``` 12 | $ php examples/ExitCodeExample.php && echo 'This ran successfully' 13 | Exiting with a code of 0 14 | This ran successfully 15 | 16 | $ php examples/ExitCodeExample.php abc || echo 'Something went wrong' 17 | Exiting with a code of 1 18 | Something went wrong 19 | ``` 20 | 21 | You may also verify the exit code via `echo $?`: 22 | 23 | ``` 24 | $ php examples/ExitCodeExample.php abc; echo $? 25 | Exiting with a code of 1 26 | 1 27 | ``` 28 | -------------------------------------------------------------------------------- /src/PhpCliTools/Counter.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 10 | * 11 | * @link http://symfony.com/doc/current/components/console 12 | */ 13 | 14 | namespace SteveGrunwell\PhpCliExamples\SymfonyConsole; 15 | 16 | use Symfony\Component\Console\Application; 17 | 18 | // Require dependencies. 19 | require_once __DIR__ . '/../../vendor/autoload.php'; 20 | 21 | // Bootstrap a Symfony Application with our command. 22 | $application = new Application(); 23 | $application->add(new HelloCommand()); 24 | $application->add(new ShowOffCommand()); 25 | $application->run(); 26 | -------------------------------------------------------------------------------- /src/SymfonyConsole/HelloCommand.php: -------------------------------------------------------------------------------- 1 | setDescription('Greet a user by name.') 26 | ->addArgument( 27 | 'name', 28 | InputArgument::REQUIRED, 29 | 'The name of the user' 30 | ); 31 | } 32 | 33 | /** 34 | * Execute the command. 35 | * 36 | * @param InputInterface $input The input interface. 37 | * @param OutputInterface $output The output interface. 38 | */ 39 | protected function execute(InputInterface $input, OutputInterface $output): int 40 | { 41 | $output->writeln(sprintf( 42 | 'Symfony says "hello", %s!', 43 | $input->getArgument('name') 44 | )); 45 | 46 | return Command::SUCCESS; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/SymfonyConsole/README.md: -------------------------------------------------------------------------------- 1 | # Symfony Console 2 | 3 | This directory contains an example [Symfony Console](https://symfony.com/doc/current/components/console.html) application with two commands, which you may interact with it by running the `App.php` file. 4 | 5 | ## [HelloCommand.php](HelloCommand.php) 6 | 7 | A simple greeter script. 8 | 9 | ``` 10 | $ php App.php hello-there Steve 11 | Symfony says "hello", Steve! 12 | ``` 13 | 14 | If you don't provide a name, the command will fail: 15 | 16 | ``` 17 | $ php App.php hello-there 18 | 19 | Not enough arguments (missing: "name"). 20 | 21 | hello-there 22 | ``` 23 | 24 | You may also see all commands registered within the app and see how to use them: 25 | 26 | ``` 27 | $ php App.php 28 | Console Tool 29 | 30 | Usage: 31 | command [options] [arguments] 32 | 33 | Options: 34 | -h, --help Display help for the given command. When no command is given display help for the list command 35 | -q, --quiet Do not output any message 36 | -V, --version Display this application version 37 | --ansi|--no-ansi Force (or disable --no-ansi) ANSI output 38 | -n, --no-interaction Do not ask any interactive question 39 | -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug 40 | 41 | Available commands: 42 | completion Dump the shell completion script 43 | hello-there Greet a user by name. 44 | help Display help for a command 45 | list List commands 46 | ``` 47 | 48 | ``` 49 | $ php App.php hello-there --help 50 | Description: 51 | Greet a user by name. 52 | 53 | Usage: 54 | hello-there 55 | 56 | Arguments: 57 | name The name of the user 58 | 59 | Options: 60 | -h, --help Display help for the given command. When no command is given display help for the list command 61 | -q, --quiet Do not output any message 62 | -V, --version Display this application version 63 | --ansi|--no-ansi Force (or disable --no-ansi) ANSI output 64 | -n, --no-interaction Do not ask any interactive question 65 | -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug 66 | ``` 67 | 68 | ## [ShowOffCommand.php](ShowOffCommand.php) 69 | 70 | This command demonstrates some of [the input and output options available via Symfony Console](https://symfony.com/doc/current/console/style.html): 71 | 72 | ``` 73 | $ php App.php show-off 74 | ``` 75 | -------------------------------------------------------------------------------- /src/SymfonyConsole/ShowOffCommand.php: -------------------------------------------------------------------------------- 1 | setDescription('Show off some of the nice output features'); 26 | } 27 | 28 | /** 29 | * Execute the command. 30 | * 31 | * @param InputInterface $input The input interface. 32 | * @param OutputInterface $output The output interface. 33 | */ 34 | protected function execute(InputInterface $input, OutputInterface $output): int 35 | { 36 | $io = new SymfonyStyle($input, $output); 37 | 38 | $io->title("Let's show off a bit, shall we?"); 39 | $io->text('This will show off some of the nice outputs available to us through Symfony Console.'); 40 | 41 | $io->section('Colors'); 42 | $io->info('This is an informational message'); 43 | $io->comment('This is a comment'); 44 | $io->warning('This is a warning'); 45 | $io->error('This is an error'); 46 | $io->success('This is a success message'); 47 | 48 | $io->section('Formatting'); 49 | $io->listing([ 50 | 'This is a list of items', 51 | 'They get passed as an array', 52 | 'Then formatted nicely', 53 | ]); 54 | $io->note('This is a note'); 55 | $io->caution('This is a cautionary message'); 56 | 57 | $io->text('Table'); 58 | $io->table( 59 | ['Animal', 'Sound'], 60 | [ 61 | ['dog', 'woof'], 62 | ['cat', 'meow'], 63 | ['bird', 'tweet'], 64 | ] 65 | ); 66 | 67 | $io->text('Horizontal table'); 68 | $io->horizontalTable( 69 | ['Animal', 'Sound'], 70 | [ 71 | ['dog', 'woof'], 72 | ['cat', 'meow'], 73 | ['bird', 'tweet'], 74 | ] 75 | ); 76 | 77 | $io->text('Definition list'); 78 | $io->definitionList( 79 | ['dog' => 'woof'], 80 | ['cat' => 'meow'], 81 | ['bird' => 'tweet'], 82 | ); 83 | 84 | $io->section('Progress bars'); 85 | $io->progressStart(5); 86 | 87 | for ($i = 0; $i < 5; $i++) { 88 | sleep(1); 89 | $io->progressAdvance(); 90 | } 91 | 92 | $io->progressFinish(); 93 | $io->success('Our progress bar completed successfully!'); 94 | 95 | $io->section('Input features'); 96 | $name = $io->ask("What's your name?", 'anonymous'); 97 | $secret = $io->askHidden("Okay, {$name}, what's a secret you have? (This won't show)"); 98 | if ($secret && $io->confirm('Do you want to know a secret?')) { 99 | $io->text("Well, according to {$name}:"); 100 | $io->newLine(); 101 | $io->text("\t{$secret}"); 102 | $io->newLine(); 103 | $io->text('Pretty wild, right?'); 104 | } 105 | 106 | return Command::SUCCESS; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/SystemCommands/Counter.php: -------------------------------------------------------------------------------- 1 | %s\n\$output => %s\n\$exit_code => %d\n", 13 | var_export($return, true), 14 | var_export($output, true), 15 | $exit_code 16 | ); 17 | -------------------------------------------------------------------------------- /src/SystemCommands/PassthruExample.php: -------------------------------------------------------------------------------- 1 | %s\n", var_export($return, true)); 12 | -------------------------------------------------------------------------------- /src/SystemCommands/README.md: -------------------------------------------------------------------------------- 1 | # System Commands 2 | 3 | This directory contains scripts demonstrating the various ways that PHP can execute other system commands. 4 | 5 | Each of these scripts runs the same command, [Counter.php](Counter.php), which takes five seconds to run and prints five lines, showing the incrementing value of the `$counter` variable. 6 | 7 | Example outputs for each of the scripts are below: 8 | 9 | ## [ExecExample.php](ExecExample.php) 10 | 11 | Demonstrates [the `exec()` function](https://www.php.net/manual/en/function.exec.php). Notice that the results aren't shown until after the five seconds have elapsed. 12 | 13 | ``` 14 | $ php ExecExample.php 15 | 16 | RESULTS: 17 | $return => '$counter = 5' 18 | $output => array ( 19 | 0 => '$counter = 1', 20 | 1 => '$counter = 2', 21 | 2 => '$counter = 3', 22 | 3 => '$counter = 4', 23 | 4 => '$counter = 5', 24 | ) 25 | $exit_code => 0 26 | ``` 27 | 28 | ## [ShellExecExample.php](ShellExecExample.php) 29 | 30 | Demonstrates [the `shell_exec()` function](https://www.php.net/manual/en/function.shell-exec.php). Notice that the results aren't shown until after the five seconds have elapsed. 31 | 32 | ``` 33 | $ php ShellExecExample.php 34 | 35 | RESULTS: 36 | $return => '$counter = 1 37 | $counter = 2 38 | $counter = 3 39 | $counter = 4 40 | $counter = 5 41 | ' 42 | ``` 43 | 44 | ## [SystemExample.php](SystemExample.php) 45 | 46 | Demonstrates [the `system()` function](https://www.php.net/manual/en/function.system.php). Notice how the output buffer is flushed with each new line. 47 | 48 | ``` 49 | $ php SystemExample.php 50 | 51 | $counter = 1 52 | $counter = 2 53 | $counter = 3 54 | $counter = 4 55 | $counter = 5 56 | 57 | RESULTS: 58 | $return => '$counter = 5' 59 | $exit_code => 0 60 | ``` 61 | 62 | ## [PassthruExample.php](PassthruExample.php) 63 | 64 | Demonstrates [the `passthru()` function](https://www.php.net/manual/en/function.passthru.php). Notice that (like system) the output buffer is flushed with each new line. 65 | 66 | ``` 67 | $ php PassthruExample.php 68 | 69 | $counter = 1 70 | $counter = 2 71 | $counter = 3 72 | $counter = 4 73 | $counter = 5 74 | 75 | RESULTS: 76 | $return => NULL 77 | ``` 78 | -------------------------------------------------------------------------------- /src/SystemCommands/ShellExecExample.php: -------------------------------------------------------------------------------- 1 | %s\n", var_export($return, true)); 12 | -------------------------------------------------------------------------------- /src/SystemCommands/SystemExample.php: -------------------------------------------------------------------------------- 1 | %s\n\$exit_code => %d\n", 13 | var_export($return, true), 14 | $exit_code 15 | ); 16 | -------------------------------------------------------------------------------- /src/WPCLIExample.php: -------------------------------------------------------------------------------- 1 | 34 | * : The user login to collect stats for. 35 | * 36 | * ## EXAMPLES 37 | * 38 | * wp example-command latest-posts-by-user admin 39 | * 40 | * @subcommand latest-posts-by-user 41 | */ 42 | public function latest_posts_by_user( $args ) { 43 | $user = get_user_by( 'login', $args['0'] ); 44 | 45 | if ( ! $user ) { 46 | return WP_CLI::error( 'The specified user login does not exist!' ); 47 | } 48 | 49 | $posts = get_posts( [ 50 | 'author' => $user->ID, 51 | ] ); 52 | 53 | return WP_CLI\Utils\format_items( 54 | 'table', 55 | $posts, 56 | [ 'ID', 'post_title', 'post_date' ] 57 | ); 58 | } 59 | } 60 | 61 | WP_CLI::add_command( 'example-command', 'Example_WP_CLI_Command' ); 62 | --------------------------------------------------------------------------------