diff --git a/docs/guide/tutorial-console.md b/docs/guide/tutorial-console.md index f955800b59..d2345d4e90 100644 --- a/docs/guide/tutorial-console.md +++ b/docs/guide/tutorial-console.md @@ -267,12 +267,21 @@ public function actionIndex() } ``` -There are some predefined constants you can use: +There are some predefined constants you can use. These are defined in the [[yii\console\ExitCode]] class: -- [[yii\console\Controller::EXIT_CODE_NORMAL|Controller::EXIT_CODE_NORMAL]] with value of `0`; -- [[yii\console\Controller::EXIT_CODE_ERROR|Controller::EXIT_CODE_ERROR]] with value of `1`. +```php +public function actionIndex() +{ + if (/* some problem */) { + echo "A problem occurred!\n"; + return ExitCode::UNSPECIFIED_ERROR; + } + // do something + return ExitCode::OK; +} +``` -It's a good practice to define meaningful constants for your controller in case you have more error code types. +It's a good practice to define meaningful constants for your controller in case you have more specific error code types. ### Formatting and colors diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index c6b7a1f9c0..651ec0afed 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -33,6 +33,7 @@ Yii Framework 2 Change Log - Enh #14105: Implemented a solution for retrieving DBMS constraints in `yii\db\Schema` (sergeymakinen) - Enh #14417: Added configuration for headers in PHP files generated by `message/extract` command (rob006) - Enh #13835: Added `yii\web\Request::getOrigin()` method that returns HTTP_ORIGIN of current CORS request (yyxx9988) +- Enh #14188: Add constants and function for sysexits(3) to `ConsoleHelper` (tom--, samdark, cebe) - Bug #14165: Set `_slave` of `Connection` to `false` instead of `null` in `close` method (rossoneri) - Bug #14423: Fixed `ArrayHelper::merge` behavior with null values for integer-keyed elements (dmirogin) diff --git a/framework/console/Controller.php b/framework/console/Controller.php index ad6ea6a9b2..ba6bf78fd0 100644 --- a/framework/console/Controller.php +++ b/framework/console/Controller.php @@ -39,7 +39,13 @@ use yii\helpers\Console; */ class Controller extends \yii\base\Controller { + /** + * @deprecated since 2.0.13. Use [[ExitCode::OK]] instead. + */ const EXIT_CODE_NORMAL = 0; + /** + * @deprecated since 2.0.13. Use [[ExitCode::UNSPECIFIED_ERROR]] instead. + */ const EXIT_CODE_ERROR = 1; /** diff --git a/framework/console/ExitCode.php b/framework/console/ExitCode.php new file mode 100644 index 0000000000..ac86fd66db --- /dev/null +++ b/framework/console/ExitCode.php @@ -0,0 +1,159 @@ +isAllowedToPerformAction()) { + * $this->stderr('Error: ' . ExitCode::getReason(ExitCode::NOPERM)); + * return ExitCode::NOPERM; + * } + * + * // do something + * + * return ExitCode::OK; + * } + * ``` + * + * @author Tom Worster + * @author Alexander Makarov + * @see http://man.openbsd.org/sysexits + * @since 2.0.13 + */ +class ExitCode +{ + /** + * The command completed successfully. + */ + const OK = 0; + /** + * The command exited with an error code that says nothing about the error. + */ + const UNSPECIFIED_ERROR = 1; + /** + * The command was used incorrectly, e.g., with the wrong number of + * arguments, a bad flag, a bad syntax in a parameter, or whatever. + */ + const USAGE = 64; + /** + * The input data was incorrect in some way. This should only be used for + * user's data and not system files. + */ + const DATAERR = 65; + /** + * An input file (not a system file) did not exist or was not readable. + * This could also include errors like ``No message'' to a mailer (if it + * cared to catch it). + */ + const NOINPUT = 66; + /** + * The user specified did not exist. This might be used for mail addresses + * or remote logins. + */ + const NOUSER = 67; + /** + * The host specified did not exist. This is used in mail addresses or + * network requests. + */ + const NOHOST = 68; + /** + * A service is unavailable. This can occur if a support program or file + * does not exist. This can also be used as a catchall message when + * something you wanted to do does not work, but you do not know why. + */ + const UNAVAILABLE = 69; + /** + * An internal software error has been detected. This should be limited to + * non-operating system related errors as possible. + */ + const SOFTWARE = 70; + /** + * An operating system error has been detected. This is intended to be + * used for such things as ``cannot fork'', ``cannot create pipe'', or the + * like. It includes things like getuid returning a user that does not + * exist in the passwd file. + */ + const OSERR = 71; + /** + * Some system file (e.g., /etc/passwd, /var/run/utx.active, etc.) does not + * exist, cannot be opened, or has some sort of error (e.g., syntax error). + */ + const OSFILE = 72; + /** + * A (user specified) output file cannot be created. + */ + const CANTCREAT = 73; + /** + * An error occurred while doing I/O on some file. + */ + const IOERR = 74; + /** + * Temporary failure, indicating something that is not really an error. In + * sendmail, this means that a mailer (e.g.) could not create a connection, + * and the request should be reattempted later. + */ + const TEMPFAIL = 75; + /** + * The remote system returned something that was ``not possible'' during a + * protocol exchange. + */ + const PROTOCOL = 76; + /** + * You did not have sufficient permission to perform the operation. This + * is not intended for file system problems, which should use NOINPUT or + * CANTCREAT, but rather for higher level permissions. + */ + const NOPERM = 77; + /** + * Something was found in an unconfigured or misconfigured state. + */ + const CONFIG = 78; + + /** + * @var array a map of reason descriptions for exit codes. + */ + public static $reasons = [ + self::OK => 'Success', + self::UNSPECIFIED_ERROR => "Unspecified error", + self::USAGE => 'Incorrect usage, argument or option error', + self::DATAERR => 'Error in input data', + self::NOINPUT => 'Input file not found or unreadable', + self::NOUSER => 'User not found', + self::NOHOST => 'Host not found', + self::UNAVAILABLE => 'A requied service is unavailable', + self::SOFTWARE => 'Internal error', + self::OSERR => 'Error making system call or using OS service', + self::OSFILE => 'Error accessing system file', + self::CANTCREAT => 'Cannot create output file', + self::IOERR => 'I/O error', + self::TEMPFAIL => 'Temporary failure', + self::PROTOCOL => 'Unexpected remote service behavior', + self::NOPERM => 'Insufficient permissions', + self::CONFIG => 'Configuration error', + ]; + + /** + * Returns a short reason text for the given exit code. + * + * This method uses [[$reasons]] to determine the reason for an exit code. + * @param int $exitCode one of the constants defined in this class. + * @return string the reason text, or `"Unknown exit code"` if the code is not listed in [[$reasons]]. + */ + public static function getReason($exitCode) + { + return isset($exReasons[$exitCode]) ? $exReasons[$exitCode] : "Unknown exit code"; + } +} diff --git a/framework/console/controllers/AssetController.php b/framework/console/controllers/AssetController.php index ac13645f2d..8d1800019d 100644 --- a/framework/console/controllers/AssetController.php +++ b/framework/console/controllers/AssetController.php @@ -10,6 +10,7 @@ namespace yii\console\controllers; use Yii; use yii\console\Controller; use yii\console\Exception; +use yii\console\ExitCode; use yii\helpers\Console; use yii\helpers\FileHelper; use yii\helpers\VarDumper; @@ -728,7 +729,7 @@ return [ EOD; if (file_exists($configFile)) { if (!$this->confirm("File '{$configFile}' already exists. Do you wish to overwrite it?")) { - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } } if (!file_put_contents($configFile, $template)) { @@ -736,7 +737,7 @@ EOD; } $this->stdout("Configuration file template created at '{$configFile}'.\n\n", Console::FG_GREEN); - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } /** diff --git a/framework/console/controllers/BaseMigrateController.php b/framework/console/controllers/BaseMigrateController.php index 36872550e2..7850828930 100644 --- a/framework/console/controllers/BaseMigrateController.php +++ b/framework/console/controllers/BaseMigrateController.php @@ -11,6 +11,7 @@ use Yii; use yii\base\InvalidConfigException; use yii\console\Controller; use yii\console\Exception; +use yii\console\ExitCode; use yii\helpers\Console; use yii\helpers\FileHelper; @@ -155,7 +156,7 @@ abstract class BaseMigrateController extends Controller if (empty($migrations)) { $this->stdout("No new migrations found. Your system is up-to-date.\n", Console::FG_GREEN); - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } $total = count($migrations); @@ -183,7 +184,7 @@ abstract class BaseMigrateController extends Controller $this->stdout("\n$applied from $n " . ($applied === 1 ? 'migration was' : 'migrations were') . " applied.\n", Console::FG_RED); $this->stdout("\nMigration failed. The rest of the migrations are canceled.\n", Console::FG_RED); - return self::EXIT_CODE_ERROR; + return ExitCode::UNSPECIFIED_ERROR; } $applied++; } @@ -225,7 +226,7 @@ abstract class BaseMigrateController extends Controller if (empty($migrations)) { $this->stdout("No migration has been done before.\n", Console::FG_YELLOW); - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } $migrations = array_keys($migrations); @@ -244,7 +245,7 @@ abstract class BaseMigrateController extends Controller $this->stdout("\n$reverted from $n " . ($reverted === 1 ? 'migration was' : 'migrations were') . " reverted.\n", Console::FG_RED); $this->stdout("\nMigration failed. The rest of the migrations are canceled.\n", Console::FG_RED); - return self::EXIT_CODE_ERROR; + return ExitCode::UNSPECIFIED_ERROR; } $reverted++; } @@ -287,7 +288,7 @@ abstract class BaseMigrateController extends Controller if (empty($migrations)) { $this->stdout("No migration has been done before.\n", Console::FG_YELLOW); - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } $migrations = array_keys($migrations); @@ -304,14 +305,14 @@ abstract class BaseMigrateController extends Controller if (!$this->migrateDown($migration)) { $this->stdout("\nMigration failed. The rest of the migrations are canceled.\n", Console::FG_RED); - return self::EXIT_CODE_ERROR; + return ExitCode::UNSPECIFIED_ERROR; } } foreach (array_reverse($migrations) as $migration) { if (!$this->migrateUp($migration)) { $this->stdout("\nMigration failed. The rest of the migrations are canceled.\n", Console::FG_RED); - return self::EXIT_CODE_ERROR; + return ExitCode::UNSPECIFIED_ERROR; } } $this->stdout("\n$n " . ($n === 1 ? 'migration was' : 'migrations were') . " redone.\n", Console::FG_GREEN); @@ -399,7 +400,7 @@ abstract class BaseMigrateController extends Controller $this->stdout("The migration history is set at $originalVersion.\nNo actual migration was performed.\n", Console::FG_GREEN); } - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } } @@ -419,7 +420,7 @@ abstract class BaseMigrateController extends Controller } } - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } } @@ -786,7 +787,7 @@ abstract class BaseMigrateController extends Controller if (strpos($migration, $version) === 0) { $this->actionUp($i + 1); - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } } @@ -800,7 +801,7 @@ abstract class BaseMigrateController extends Controller $this->actionDown($i); } - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } } diff --git a/framework/console/controllers/CacheController.php b/framework/console/controllers/CacheController.php index 31d5383a8b..7e17f9c7eb 100644 --- a/framework/console/controllers/CacheController.php +++ b/framework/console/controllers/CacheController.php @@ -12,6 +12,7 @@ use yii\caching\ApcCache; use yii\caching\CacheInterface; use yii\console\Controller; use yii\console\Exception; +use yii\console\ExitCode; use yii\helpers\Console; /** @@ -88,11 +89,11 @@ class CacheController extends Controller if (!$foundCaches) { $this->notifyNoCachesFound(); - return static::EXIT_CODE_NORMAL; + return ExitCode::OK; } if (!$this->confirmFlush($foundCaches)) { - return static::EXIT_CODE_NORMAL; + return ExitCode::OK; } foreach ($caches as $name => $class) { @@ -116,7 +117,7 @@ class CacheController extends Controller if (empty($caches)) { $this->notifyNoCachesFound(); - return static::EXIT_CODE_NORMAL; + return ExitCode::OK; } foreach ($caches as $name => $class) { @@ -150,14 +151,14 @@ class CacheController extends Controller $connection = Yii::$app->get($db, false); if ($connection === null) { $this->stdout("Unknown component \"$db\".\n", Console::FG_RED); - return self::EXIT_CODE_ERROR; + return ExitCode::UNSPECIFIED_ERROR; } if (!$connection instanceof \yii\db\Connection) { $this->stdout("\"$db\" component doesn't inherit \\yii\\db\\Connection.\n", Console::FG_RED); - return self::EXIT_CODE_ERROR; + return ExitCode::UNSPECIFIED_ERROR; } elseif (!$this->confirm("Flush cache schema for \"$db\" connection?")) { - return static::EXIT_CODE_NORMAL; + return ExitCode::OK; } try { diff --git a/framework/console/controllers/FixtureController.php b/framework/console/controllers/FixtureController.php index 9ebfee63b3..f47bd79d4b 100644 --- a/framework/console/controllers/FixtureController.php +++ b/framework/console/controllers/FixtureController.php @@ -12,6 +12,7 @@ use yii\base\InvalidConfigException; use yii\base\InvalidParamException; use yii\console\Controller; use yii\console\Exception; +use yii\console\ExitCode; use yii\helpers\Console; use yii\helpers\FileHelper; use yii\test\FixtureTrait; @@ -112,7 +113,7 @@ class FixtureController extends Controller $helpCommand = Console::ansiFormat('yii help fixture', [Console::FG_CYAN]); $this->stdout("Use $helpCommand to get usage info.\n"); - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } $filtered = $this->filterFixtures($fixturesInput); @@ -142,11 +143,11 @@ class FixtureController extends Controller if (!$fixturesToLoad) { $this->notifyNothingToLoad($foundFixtures, $except); - return static::EXIT_CODE_NORMAL; + return ExitCode::OK; } if (!$this->confirmLoad($fixturesToLoad, $except)) { - return static::EXIT_CODE_NORMAL; + return ExitCode::OK; } $fixtures = $this->getFixturesConfig(array_merge($this->globalFixtures, $fixturesToLoad)); @@ -161,7 +162,7 @@ class FixtureController extends Controller $this->loadFixtures($fixturesObjects); $this->notifyLoaded($fixtures); - return static::EXIT_CODE_NORMAL; + return ExitCode::OK; } /** @@ -212,11 +213,11 @@ class FixtureController extends Controller if (!$fixturesToUnload) { $this->notifyNothingToUnload($foundFixtures, $except); - return static::EXIT_CODE_NORMAL; + return ExitCode::OK; } if (!$this->confirmUnload($fixturesToUnload, $except)) { - return static::EXIT_CODE_NORMAL; + return ExitCode::OK; } $fixtures = $this->getFixturesConfig(array_merge($this->globalFixtures, $fixturesToUnload)); diff --git a/framework/console/controllers/MessageController.php b/framework/console/controllers/MessageController.php index a2bf2e86c2..4dd9af441f 100644 --- a/framework/console/controllers/MessageController.php +++ b/framework/console/controllers/MessageController.php @@ -9,6 +9,7 @@ namespace yii\console\controllers; use Yii; use yii\console\Exception; +use yii\console\ExitCode; use yii\db\Connection; use yii\db\Query; use yii\di\Instance; @@ -219,7 +220,7 @@ class MessageController extends \yii\console\Controller $filePath = Yii::getAlias($filePath); if (file_exists($filePath)) { if (!$this->confirm("File '{$filePath}' already exists. Do you wish to overwrite it?")) { - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } } @@ -242,11 +243,11 @@ EOD; if (file_put_contents($filePath, $content) === false) { $this->stdout("Configuration file was NOT created: '{$filePath}'.\n\n", Console::FG_RED); - return self::EXIT_CODE_ERROR; + return ExitCode::UNSPECIFIED_ERROR; } $this->stdout("Configuration file created: '{$filePath}'.\n\n", Console::FG_GREEN); - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } /** @@ -266,17 +267,17 @@ EOD; if (file_exists($filePath)) { if (!$this->confirm("File '{$filePath}' already exists. Do you wish to overwrite it?")) { - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } } if (!copy(Yii::getAlias('@yii/views/messageConfig.php'), $filePath)) { $this->stdout("Configuration file template was NOT created at '{$filePath}'.\n\n", Console::FG_RED); - return self::EXIT_CODE_ERROR; + return ExitCode::UNSPECIFIED_ERROR; } $this->stdout("Configuration file template created at '{$filePath}'.\n\n", Console::FG_GREEN); - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } /** @@ -677,7 +678,7 @@ EOD; ksort($existingMessages); if (array_keys($existingMessages) === $messages && (!$sort || array_keys($rawExistingMessages) === $messages)) { $this->stdout("Nothing new in \"$category\" category... Nothing to save.\n\n", Console::FG_GREEN); - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } unset($rawExistingMessages); $merged = []; @@ -731,11 +732,11 @@ EOD; if (file_put_contents($fileName, $content) === false) { $this->stdout("Translation was NOT saved.\n\n", Console::FG_RED); - return self::EXIT_CODE_ERROR; + return ExitCode::UNSPECIFIED_ERROR; } $this->stdout("Translation saved.\n\n", Console::FG_GREEN); - return self::EXIT_CODE_NORMAL; + return ExitCode::OK; } /**