aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Kasimir Cash <kasimir.cash@outlook.com> 2024-01-17 07:42:43 +0000
committerGravatar GitHub <noreply@github.com> 2024-01-17 08:42:43 +0100
commit6d14813840d163c76f6dc25395b0007a88b42e9d (patch)
treed01fccbd7e31f4fdccfc6649621ba1d8d75a9eef
parent314077a457f04cc2f0472e036af029e2676fbf02 (diff)
Standardise command line option parsing (#6036)
* Separates long & short options for parsing * Adds parsing for short options + doc rewrites * Fixes undefined constant in check.translation * Standardises CL option parsing * Refactors option parsing * Renames getLongOptions -> getOptions * Removes unused code * Converges on string typing for options * Updates docs & help files * Updates array syntax array( ) -> [ ]
-rw-r--r--cli/README.md100
-rw-r--r--cli/_cli.php113
-rw-r--r--cli/_update-or-create-user.php15
-rwxr-xr-xcli/actualize-user.php16
-rwxr-xr-xcli/check.translation.php41
-rwxr-xr-xcli/db-optimize.php16
-rwxr-xr-xcli/delete-user.php16
-rwxr-xr-xcli/do-install.php24
-rwxr-xr-xcli/export-opml-for-user.php16
-rwxr-xr-xcli/export-sqlite-for-user.php21
-rwxr-xr-xcli/export-zip-for-user.php24
-rwxr-xr-xcli/import-for-user.php27
-rwxr-xr-xcli/import-sqlite-for-user.php25
-rwxr-xr-xcli/manipulate.translation.php106
-rwxr-xr-xcli/reconfigure.php21
-rwxr-xr-xcli/user-info.php42
16 files changed, 352 insertions, 271 deletions
diff --git a/cli/README.md b/cli/README.md
index 028bfbc8f..1ce70b5c1 100644
--- a/cli/README.md
+++ b/cli/README.md
@@ -35,22 +35,22 @@ cd /usr/share/FreshRSS
# Ensure the needed directories in ./data/
./cli/do-install.php --default-user admin [ --auth-type form --environment production --base-url https://rss.example.net --language en --title FreshRSS --allow-anonymous --allow-anonymous-refresh --api-enabled --db-type sqlite --db-host localhost:3306 --db-user freshrss --db-password dbPassword123 --db-base freshrss --db-prefix freshrss_ ]
-# --default-user must be alphanumeric and not longer than 38 characters. The default user of this FreshRSS instance, used as the public user for anonymous reading
-# --auth-type can be: 'form' (default), 'http_auth' (using the Web server access control), 'none' (dangerous)
-# --environment can be: 'production' (default), 'development' (for additional log messages)
-# --base-url should be a public (routable) URL if possible, and is used for push (WebSub), for some API functions (e.g. favicons), and external URLs in FreshRSS
-# --language can be: 'en' (default), 'fr', or one of the [supported languages](../app/i18n/)
-# --title web user interface title for this FreshRSS instance
-# --allow-anonymous sets whether non logged-in visitors are permitted to see the default user's feeds
-# --allow-anonymous-refresh sets whether to permit anonymous users to start the refresh process
-# --api-enabled sets whether the API may be used for mobile apps. API passwords must be set for individual users
-# --db-type can be: 'sqlite' (default), 'mysql' (MySQL or MariaDB), 'pgsql' (PostgreSQL)
-# --db-host URL of the database server. Default is 'localhost'
-# --db-user sets database user
-# --db-password sets database password
-# --db-base sets database name
-# --db-prefix is an optional prefix in front of the names of the tables. We suggest using 'freshrss_' (default)
-# This command does not create the default user. Do that with ./cli/create-user.php
+# --default-user must be alphanumeric and not longer than 38 characters. The default user of this FreshRSS instance, used as the public user for anonymous reading.
+# --auth-type can be: 'form' (default), 'http_auth' (using the Web server access control), 'none' (dangerous).
+# --environment can be: 'production' (default), 'development' (for additional log messages).
+# --base-url should be a public (routable) URL if possible, and is used for push (WebSub), for some API functions (e.g. favicons), and external URLs in FreshRSS.
+# --language can be: 'en' (default), 'fr', or one of the [supported languages](../app/i18n/).
+# --title web user interface title for this FreshRSS instance.
+# --allow-anonymous sets whether non logged-in visitors are permitted to see the default user's feeds.
+# --allow-anonymous-refresh sets whether to permit anonymous users to start the refresh process.
+# --api-enabled sets whether the API may be used for mobile apps. API passwords must be set for individual users.
+# --db-type can be: 'sqlite' (default), 'mysql' (MySQL or MariaDB), 'pgsql' (PostgreSQL).
+# --db-host URL of the database server. Default is 'localhost'.
+# --db-user sets database user.
+# --db-password sets database password.
+# --db-base sets database name.
+# --db-prefix is an optional prefix in front of the names of the tables. We suggest using 'freshrss_' (default).
+# This command does not create the default user. Do that with ./cli/create-user.php.
./cli/reconfigure.php
# Same parameters as for do-install.php. Used to update an existing installation.
@@ -64,51 +64,52 @@ cd /usr/share/FreshRSS
cd /usr/share/FreshRSS
./cli/create-user.php --user username [ --password 'password' --api-password 'api_password' --language en --email user@example.net --token 'longRandomString' --no-default-feeds --purge-after-months 3 --feed-min-articles-default 50 --feed-ttl-default 3600 --since-hours-posts-per-rss 168 --max-posts-per-rss 400 ]
-# --user must be alphanumeric, not longer than 38 characters. The name of the user to be created/updated
-# --password sets the user's password
-# --api-password sets the user's api password
-# --language can be: 'en' (default), 'fr', or one of the [supported languages](../app/i18n/)
-# --email sets an email for the user which will be used email validation if it forced email validation is enabled
-# --no-default-feeds do not add this FreshRSS instance's default feeds to the user during creation
-# --purge-after-months max age an article can reach before being archived. Default is '3'
-# --feed-min-articles-default number of articles in a feed at which archiving will pause. Default is '50'
-# --feed-ttl-default minimum number of seconds to elapse between feed refreshes. Default is '3600'
-# --max-posts-per-rss number of articles in a feed at which an old article will be archived before a new article is added. Default is '200'
+# --user must be alphanumeric, not longer than 38 characters. The name of the user to be created/updated.
+# --password sets the user's password.
+# --api-password sets the user's api password.
+# --language can be: 'en' (default), 'fr', or one of the [supported languages](../app/i18n/).
+# --email sets an email for the user which will be used email validation if it forced email validation is enabled.
+# --no-default-feeds do not add this FreshRSS instance's default feeds to the user during creation.
+# --purge-after-months max age an article can reach before being archived. Default is '3'.
+# --feed-min-articles-default number of articles in a feed at which archiving will pause. Default is '50'.
+# --feed-ttl-default minimum number of seconds to elapse between feed refreshes. Default is '3600'.
+# --max-posts-per-rss number of articles in a feed at which an old article will be archived before a new article is added. Default is '200'.
./cli/update-user.php --user username [ ... ]
-# Same options as create-user.php, except --no-default-feeds which is only available for create-user.php
+# Same options as create-user.php, except --no-default-feeds which is only available for create-user.php.
```
> ℹ️ More options for [the configuration of users](../config-user.default.php#L3-L5) may be set in `./data/config-user.custom.php` prior to creating new users, or in `./data/users/*/config.php` for existing users.
```sh
./cli/actualize-user.php --user username
-# Fetch feeds for the specified user
+# Fetch feeds for the specified user.
./cli/delete-user.php --user username
+# Deletes the specified user.
./cli/list-users.php
-# Return a list of users, with the default/admin user first
+# Return a list of users, with the default/admin user first.
-./cli/user-info.php [ -h --header --json --user username1 --user username2 ... ]
-# -h is to use a human-readable format
-# --header outputs some columns headers
-# --json JSON format (disables --header and -h but uses ISO Zulu format for dates)
-# --user indicates a username, and can be repeated
+./cli/user-info.php [ --human-readable --header --json --user username1 --user username2 ... ]
+# -h, --human-readable display output in a human readable format
+# --header outputs some columns headers.
+# --json JSON format (disables --header and --human-readable but uses ISO Zulu format for dates).
+# --user indicates a username, and can be repeated.
# Returns: 1) a * if the user is admin, 2) the name of the user,
# 3) the date/time of last user action, 4) the size occupied,
# and the number of: 5) categories, 6) feeds, 7) read articles, 8) unread articles, 9) favourites, 10) tags,
-# 11) language, 12) e-mail
+# 11) language, 12) e-mail.
./cli/import-for-user.php --user username --filename /path/to/file.ext
-# The extension of the file { .json, .opml, .xml, .zip } is used to detect the type of import
+# The extension of the file { .json, .opml, .xml, .zip } is used to detect the type of import.
./cli/export-sqlite-for-user.php --user username --filename /path/to/db.sqlite
# Export the user’s database to a new SQLite file.
./cli/import-sqlite-for-user.php --user username [ --force-overwrite ] --filename /path/to/db.sqlite
# Import the user’s database from an SQLite file.
-# --force-overwrite will clear the target user database before import (import only works on an empty user database)
+# --force-overwrite will clear the target user database before import (import only works on an empty user database).
./cli/export-opml-for-user.php --user username > /path/to/file.opml.xml
@@ -129,15 +130,22 @@ cd /usr/share/FreshRSS
```sh
cd /usr/share/FreshRSS
-./cli/manipulate.translation.php --a [-h --a --k --v --l --o]
-# manipulate the i18n language files
-# -h is to use a human-readable format
-# --a selects the action to perform. (can be: add, delete, exist, format, and ignore.
-# --k selects the key to work on.
-# --v selects the value to set.
-# --l selects the language to work on.
-# --r revert the action (only for ignore action)
-# --o selects the origin language (only for add language action)
+./cli/manipulate.translation.php --action [ --help --key --value --language --revert --origin-language ]
+# manipulate translation files.
+# -a, --action selects the action to perform. (can be either: add, delete, exist, format, or ignore)
+# -h, --help displays the commands help file.
+# -k, --key selects the key to work on.
+# -v, --value selects the value to set.
+# -l, --language selects the language to work on.
+# -r, --revert revert the action (only used with ignore action).
+# -o, --origin-language selects the origin language (only used with add language action).
+
+./cli/check-translation.php [ ---display-result --help --language fr --display-report ]
+# Check if translation files have missing keys or missing translations.
+# -d, --display-result display results of check.
+# -h, --help display help text and exit.
+# -l, --language set the language check.
+# -r, --display-report display completion report.
```
## Note about cron
diff --git a/cli/_cli.php b/cli/_cli.php
index 9ba6ec006..c51dd69a3 100644
--- a/cli/_cli.php
+++ b/cli/_cli.php
@@ -6,8 +6,7 @@ if (php_sapi_name() !== 'cli') {
}
const EXIT_CODE_ALREADY_EXISTS = 3;
-const REGEX_INPUT_OPTIONS = '/^--/';
-const REGEX_PARAM_OPTIONS = '/:*$/';
+const REGEX_INPUT_OPTIONS = '/^-{2}|^-{1}/';
require(__DIR__ . '/../constants.php');
require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader
@@ -77,37 +76,43 @@ function performRequirementCheck(string $databaseType): void {
/**
* Parses parameters used with FreshRSS' CLI commands.
- * @param array{'valid':array<string,string>,'deprecated':array<string,string>} $parameters An array of 'valid': An
- * array of parameters as keys and their respective getopt() notations as values. 'deprecated' An array with
- * replacement parameters as keys and their respective deprecated parameters as values.
- * @return array{'valid':array<string,string|bool>,'invalid':array<string>} An array of 'valid': an array of all
- * known parameters used and their respective options and 'invalid': an array of all unknown parameters used.
+ * @param array{'long':array<string,string>,'short':array<string,string>,'deprecated':array<string,string>} $parameters
+ * Matrix of 'long': map of long option names as keys and their respective getopt() notations as values,
+ * 'short': map of short option names as values and their equivalent long options as keys, 'deprecated': map of
+ * replacement option names as keys and their respective deprecated option names as values.
+ * @return array{'valid':array<string,string>,'invalid':array<string>} Matrix of 'valid': map of of all known
+ * option names used and their respective values and 'invalid': list of all unknown options used.
*/
function parseCliParams(array $parameters): array {
global $argv;
- $cliParams = [];
+ $longOptions = [];
+ $shortOptions = '';
- foreach ($parameters['valid'] as $param => $getopt_val) {
- $cliParams[] = $param . $getopt_val;
+ foreach ($parameters['long'] as $name => $getopt_note) {
+ $longOptions[] = $name . $getopt_note;
}
- foreach ($parameters['deprecated'] as $param => $deprecatedParam) {
- $cliParams[] = $deprecatedParam . $parameters['valid'][$param];
+ foreach ($parameters['deprecated'] as $name => $deprecatedName) {
+ $longOptions[] = $deprecatedName . $parameters['long'][$name];
+ }
+ foreach ($parameters['short'] as $name => $shortName) {
+ $shortOptions .= $shortName . $parameters['long'][$name];
}
- $opts = getopt('', $cliParams);
+ $options = getopt($shortOptions, $longOptions);
- /** @var array<string,string|bool> $valid */
- $valid = is_array($opts) ? $opts : [];
+ $valid = is_array($options) ? $options : [];
- array_walk($valid, static fn(&$option) => $option = $option === false ? true : $option);
+ array_walk($valid, static fn(&$option) => $option = $option === false ? '' : $option);
- if (checkforDeprecatedParameterUse(array_keys($valid), $parameters['deprecated'])) {
- $valid = updateDeprecatedParameters($valid, $parameters['deprecated']);
- }
+ /** @var array<string,string> $valid */
+ checkForDeprecatedOptions(array_keys($valid), $parameters['deprecated']);
+
+ $valid = replaceOptions($valid, $parameters['short']);
+ $valid = replaceOptions($valid, $parameters['deprecated']);
$invalid = findInvalidOptions(
$argv,
- array_merge(array_keys($parameters['valid']), array_values($parameters['deprecated']))
+ array_merge(array_keys($parameters['long']), array_values($parameters['short']), array_values($parameters['deprecated']))
);
return [
@@ -120,7 +125,7 @@ function parseCliParams(array $parameters): array {
* @param array<string> $options
* @return array<string>
*/
-function getLongOptions(array $options, string $regex): array {
+function getOptions(array $options, string $regex): array {
$longOptions = array_filter($options, static function (string $a) use ($regex) {
return preg_match($regex, $a) === 1;
});
@@ -130,30 +135,13 @@ function getLongOptions(array $options, string $regex): array {
}
/**
- * @param array<string> $input
- * @param array<string> $params
- */
-function validateOptions(array $input, array $params): bool {
- $sanitizeInput = getLongOptions($input, REGEX_INPUT_OPTIONS);
- $sanitizeParams = getLongOptions($params, REGEX_PARAM_OPTIONS);
- $unknownOptions = array_diff($sanitizeInput, $sanitizeParams);
-
- if (0 === count($unknownOptions)) {
- return true;
- }
-
- fwrite(STDERR, sprintf("FreshRSS error: unknown options: %s\n", implode (', ', $unknownOptions)));
- return false;
-}
-
-/**
- * Checks for use of unknown parameters with FreshRSS' CLI commands.
- * @param array<string> $input An array of parameters to check for validity.
- * @param array<string> $params An array of valid parameters to check against.
- * @return array<string> Returns an array of all unknown parameters found.
+ * Checks for presence of unknown options.
+ * @param array<string> $input List of command line arguments to check for validity.
+ * @param array<string> $params List of valid options to check against.
+ * @return array<string> Returns a list all unknown options found.
*/
function findInvalidOptions(array $input, array $params): array {
- $sanitizeInput = getLongOptions($input, REGEX_INPUT_OPTIONS);
+ $sanitizeInput = getOptions($input, REGEX_INPUT_OPTIONS);
$unknownOptions = array_diff($sanitizeInput, $params);
if (0 === count($unknownOptions)) {
@@ -165,15 +153,14 @@ function findInvalidOptions(array $input, array $params): array {
}
/**
- * Checks for use of deprecated parameters with FreshRSS' CLI commands.
- * @param array<string> $options User inputs to check for deprecated parameter use.
- * @param array<string,string> $params An array with replacement parameters as keys and their respective deprecated
- * parameters as values.
- * @return bool Returns TRUE and generates a deprecation warning if deprecated parameters
- * have been used, FALSE otherwise.
+ * Checks for presence of deprecated options.
+ * @param array<string> $optionNames Command line option names to check for deprecation.
+ * @param array<string,string> $params Map of replacement options as keys and their respective deprecated
+ * options as values.
+ * @return bool Returns TRUE and generates a deprecation warning if deprecated options are present, FALSE otherwise.
*/
-function checkforDeprecatedParameterUse(array $options, array $params): bool {
- $deprecatedOptions = array_intersect($options, $params);
+function checkForDeprecatedOptions(array $optionNames, array $params): bool {
+ $deprecatedOptions = array_intersect($optionNames, $params);
$replacements = array_map(static fn($option) => array_search($option, $params, true), $deprecatedOptions);
if (0 === count($deprecatedOptions)) {
@@ -187,25 +174,17 @@ function checkforDeprecatedParameterUse(array $options, array $params): bool {
}
/**
- * Switches all used deprecated parameters to their replacements if they have one.
- *
- * @template T
- *
- * @param array<string,T> $options User inputs.
- * @param array<string,string> $params An array with replacement parameters as keys and their respective deprecated
- * parameters as values.
- * @return array<string,T> Returns $options with deprications replaced.
+ * Switches items in a list to their provided replacements.
+ * @param array<string,string> $options Map with items to check for replacement as keys.
+ * @param array<string,string> $replacements Map of replacement items as keys and the item they replace as their values.
+ * @return array<string,string> Returns $options with replacements.
*/
-function updateDeprecatedParameters(array $options, array $params): array {
+function replaceOptions(array $options, array $replacements): array {
$updatedOptions = [];
- foreach ($options as $param => $option) {
- $replacement = array_search($param, $params, true);
- if (is_string($replacement)) {
- $updatedOptions[$replacement] = $option;
- } else {
- $updatedOptions[$param] = $option;
- }
+ foreach ($options as $name => $value) {
+ $replacement = array_search($name, $replacements, true);
+ $updatedOptions[$replacement ? $replacement : $name] = $value;
}
return $updatedOptions;
diff --git a/cli/_update-or-create-user.php b/cli/_update-or-create-user.php
index 23666e3ae..1cc08bd25 100644
--- a/cli/_update-or-create-user.php
+++ b/cli/_update-or-create-user.php
@@ -4,8 +4,8 @@ require(__DIR__ . '/_cli.php');
performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? '');
-$parameters = array(
- 'valid' => array(
+$parameters = [
+ 'long' => [
'user' => ':',
'password' => ':',
'api-password' => ':',
@@ -17,21 +17,22 @@ $parameters = array(
'feed-ttl-default' => ':',
'since-hours-posts-per-rss' => ':',
'max-posts-per-rss' => ':',
- ),
- 'deprecated' => array(
+ ],
+ 'short' => [],
+ 'deprecated' => [
'api-password' => 'api_password',
'purge-after-months' => 'purge_after_months',
'feed-min-articles-default' => 'feed_min_articles_default',
'feed-ttl-default' => 'feed_ttl_default',
'since-hours-posts-per-rss' => 'since_hours_posts_per_rss',
'max-posts-per-rss' => 'max_posts_per_rss',
- ),
-);
+ ],
+];
if (!isset($isUpdate)) {
$isUpdate = false;
} elseif (!$isUpdate) {
- $parameters['valid']['no-default-feeds'] = ''; //Only for creating new users
+ $parameters['long']['no-default-feeds'] = ''; //Only for creating new users
$parameters['deprecated']['no-default-feeds'] = 'no_default_feeds';
}
diff --git a/cli/actualize-user.php b/cli/actualize-user.php
index 99b48116d..f2fae116c 100755
--- a/cli/actualize-user.php
+++ b/cli/actualize-user.php
@@ -5,17 +5,21 @@ require(__DIR__ . '/_cli.php');
performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? '');
-$params = array(
- 'user:',
-);
+$parameters = [
+ 'long' => [
+ 'user' => ':'
+ ],
+ 'short' => [],
+ 'deprecated' => [],
+];
-$options = getopt('', $params);
+$options = parseCliParams($parameters);
-if (!validateOptions($argv, $params) || empty($options['user']) || !is_string($options['user'])) {
+if (!empty($options['invalid']) || empty($options['valid']['user']) || !is_string($options['valid']['user'])) {
fail('Usage: ' . basename(__FILE__) . " --user username");
}
-$username = cliInitUser($options['user']);
+$username = cliInitUser($options['valid']['user']);
Minz_ExtensionManager::callHookVoid('freshrss_user_maintenance');
diff --git a/cli/check.translation.php b/cli/check.translation.php
index 747a35be9..10a346dee 100755
--- a/cli/check.translation.php
+++ b/cli/check.translation.php
@@ -1,28 +1,45 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
+require_once __DIR__ . '/_cli.php';
require_once __DIR__ . '/i18n/I18nCompletionValidator.php';
require_once __DIR__ . '/i18n/I18nData.php';
require_once __DIR__ . '/i18n/I18nFile.php';
require_once __DIR__ . '/i18n/I18nUsageValidator.php';
+require_once __DIR__ . '/../constants.php';
$i18nFile = new I18nFile();
$i18nData = new I18nData($i18nFile->load());
-/** @var array<string,string>|false $options */
-$options = getopt('dhl:r');
-
-if (!is_array($options) || array_key_exists('h', $options)) {
+$parameters = [
+ 'long' => [
+ 'display-result' => '',
+ 'help' => '',
+ 'language' => ':',
+ 'display-report' => '',
+ ],
+ 'short' => [
+ 'display-result' => 'd',
+ 'help' => 'h',
+ 'language' => 'l',
+ 'display-report' => 'r',
+ ],
+ 'deprecated' => [],
+];
+
+$options = parseCliParams($parameters);
+
+if (!empty($options['invalid']) || array_key_exists('help', $options['valid'])) {
checkHelp();
}
-if (array_key_exists('l', $options)) {
- $languages = array($options['l']);
+if (array_key_exists('language', $options['valid'])) {
+ $languages = [$options['valid']['language']];
} else {
$languages = $i18nData->getAvailableLanguages();
}
-$displayResults = array_key_exists('d', $options);
-$displayReport = array_key_exists('r', $options);
+$displayResults = array_key_exists('display-result', $options['valid']);
+$displayReport = array_key_exists('display-report', $options['valid']);
$isValidated = true;
$result = [];
@@ -99,10 +116,10 @@ SYNOPSIS
DESCRIPTION
Check if translation files have missing keys or missing translations.
- -d display results.
- -h display this help and exit.
- -l=LANG filter by LANG.
- -r display completion report.
+ -d, --display-result display results.
+ -h, --help display this help and exit.
+ -l, --language=LANG filter by LANG.
+ -r, --display-report display completion report.
HELP;
exit;
diff --git a/cli/db-optimize.php b/cli/db-optimize.php
index 82f545420..d553b64d9 100755
--- a/cli/db-optimize.php
+++ b/cli/db-optimize.php
@@ -5,17 +5,21 @@ require(__DIR__ . '/_cli.php');
performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? '');
-$params = array(
- 'user:',
-);
+$parameters = [
+ 'long' => [
+ 'user' => ':',
+ ],
+ 'short' => [],
+ 'deprecated' => [],
+];
-$options = getopt('', $params);
+$options = parseCliParams($parameters);
-if (!validateOptions($argv, $params) || empty($options['user']) || !is_string($options['user'])) {
+if (!empty($options['invalid']) || empty($options['valid']['user']) || !is_string($options['valid']['user'])) {
fail('Usage: ' . basename(__FILE__) . " --user username");
}
-$username = cliInitUser($options['user']);
+$username = cliInitUser($options['valid']['user']);
echo 'FreshRSS optimizing database for user “', $username, "”…\n";
diff --git a/cli/delete-user.php b/cli/delete-user.php
index d8d3033cc..b4f042847 100755
--- a/cli/delete-user.php
+++ b/cli/delete-user.php
@@ -5,16 +5,20 @@ require(__DIR__ . '/_cli.php');
performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? '');
-$params = array(
- 'user:',
-);
+$parameters = [
+ 'long' => [
+ 'user' => ':',
+ ],
+ 'short' => [],
+ 'deprecated' => [],
+];
-$options = getopt('', $params);
+$options = parseCliParams($parameters);
-if (!validateOptions($argv, $params) || empty($options['user']) || !is_string($options['user'])) {
+if (!empty($options['invalid']) || empty($options['valid']['user']) || !is_string($options['valid']['user'])) {
fail('Usage: ' . basename(__FILE__) . " --user username");
}
-$username = $options['user'];
+$username = $options['valid']['user'];
if (!FreshRSS_user_Controller::checkUsername($username)) {
fail('FreshRSS error: invalid username “' . $username . '”');
}
diff --git a/cli/do-install.php b/cli/do-install.php
index 3e9c59162..2d73c2e98 100755
--- a/cli/do-install.php
+++ b/cli/do-install.php
@@ -7,8 +7,8 @@ if (file_exists(DATA_PATH . '/applied_migrations.txt')) {
fail('FreshRSS seems to be already installed!' . "\n" . 'Please use `./cli/reconfigure.php` instead.', EXIT_CODE_ALREADY_EXISTS);
}
-$parameters = array(
- 'valid' => array(
+$parameters = [
+ 'long' => [
'environment' => ':',
'base-url' => ':',
'language' => ':',
@@ -26,8 +26,9 @@ $parameters = array(
'db-password' => ':',
'db-base' => ':',
'db-prefix' => '::',
- ),
- 'deprecated' => array(
+ ],
+ 'short' => [],
+ 'deprecated' => [
'base-url' => 'base_url',
'default-user' => 'default_user',
'allow-anonymous' => 'allow_anonymous',
@@ -36,10 +37,10 @@ $parameters = array(
'api-enabled' => 'api_enabled',
'allow-robots' => 'allow_robots',
'disable-update' => 'disable_update',
- ),
-);
+ ],
+];
-$configParams = array(
+$configParams = [
'environment' => 'environment',
'base-url' => 'base_url',
'language' => 'language',
@@ -51,16 +52,16 @@ $configParams = array(
'api-enabled' => 'api_enabled',
'allow-robots' => 'allow_robots',
'disable-update' => 'disable_update',
-);
+];
-$dBconfigParams = array(
+$dBconfigParams = [
'db-type' => 'type',
'db-host' => 'host',
'db-user' => 'user',
'db-password' => 'password',
'db-base' => 'base',
'db-prefix' => 'prefix',
-);
+];
$options = parseCliParams($parameters);
@@ -89,7 +90,8 @@ if (file_exists($customConfigPath)) {
foreach ($configParams as $param => $configParam) {
if (isset($options['valid'][$param])) {
- $config[$configParam] = $options['valid'][$param];
+ $isFlag = $parameters['long'][$param] === '';
+ $config[$configParam] = $isFlag ? true : $options['valid'][$param];
}
}
diff --git a/cli/export-opml-for-user.php b/cli/export-opml-for-user.php
index 51beb12f3..7c59c65ef 100755
--- a/cli/export-opml-for-user.php
+++ b/cli/export-opml-for-user.php
@@ -5,17 +5,21 @@ require(__DIR__ . '/_cli.php');
performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? '');
-$params = array(
- 'user:',
-);
+$parameters = [
+ 'long' => [
+ 'user' => ':',
+ ],
+ 'short' => [],
+ 'deprecated' => [],
+];
-$options = getopt('', $params);
+$options = parseCliParams($parameters);
-if (!validateOptions($argv, $params) || empty($options['user']) || !is_string($options['user'])) {
+if (!empty($options['invalid']) || empty($options['valid']['user']) || !is_string($options['valid']['user'])) {
fail('Usage: ' . basename(__FILE__) . " --user username > /path/to/file.opml.xml");
}
-$username = cliInitUser($options['user']);
+$username = cliInitUser($options['valid']['user']);
fwrite(STDERR, 'FreshRSS exporting OPML for user “' . $username . "”…\n");
diff --git a/cli/export-sqlite-for-user.php b/cli/export-sqlite-for-user.php
index ee8e183f6..e67896df9 100755
--- a/cli/export-sqlite-for-user.php
+++ b/cli/export-sqlite-for-user.php
@@ -5,19 +5,26 @@ require(__DIR__ . '/_cli.php');
performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? '');
-$params = [
- 'user:',
- 'filename:',
+$parameters = [
+ 'long' => [
+ 'user' => ':',
+ 'filename' => ':',
+ ],
+ 'short' => [],
+ 'deprecated' => [],
];
-$options = getopt('', $params);
+$options = parseCliParams($parameters);
-if (!validateOptions($argv, $params) || empty($options['user']) || empty($options['filename']) || !is_string($options['user']) || !is_string($options['filename'])) {
+if (!empty($options['invalid'])
+ || empty($options['valid']['user']) || empty($options['valid']['filename'])
+ || !is_string($options['valid']['user']) || !is_string($options['valid']['filename'])
+) {
fail('Usage: ' . basename(__FILE__) . ' --user username --filename /path/to/db.sqlite');
}
-$username = cliInitUser($options['user']);
-$filename = $options['filename'];
+$username = cliInitUser($options['valid']['user']);
+$filename = $options['valid']['filename'];
if (pathinfo($filename, PATHINFO_EXTENSION) !== 'sqlite') {
fail('Only *.sqlite files are supported!');
diff --git a/cli/export-zip-for-user.php b/cli/export-zip-for-user.php
index 5458784bf..d818096e4 100755
--- a/cli/export-zip-for-user.php
+++ b/cli/export-zip-for-user.php
@@ -5,14 +5,18 @@ require(__DIR__ . '/_cli.php');
performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? '');
-$params = array(
- 'user:',
- 'max-feed-entries:',
-);
-
-$options = getopt('', $params);
-
-if (!validateOptions($argv, $params) || empty($options['user']) || !is_string($options['user'])) {
+$parameters = [
+ 'long' => [
+ 'user' => ':',
+ 'max-feed-entries' => ':',
+ ],
+ 'short' => [],
+ 'deprecated' => [],
+];
+
+$options = parseCliParams($parameters);
+
+if (!empty($options['invalid']) || empty($options['valid']['user']) || !is_string($options['valid']['user'])) {
fail('Usage: ' . basename(__FILE__) . " --user username ( --max-feed-entries 100 ) > /path/to/file.zip");
}
@@ -20,12 +24,12 @@ if (!extension_loaded('zip')) {
fail('FreshRSS error: Lacking php-zip extension!');
}
-$username = cliInitUser($options['user']);
+$username = cliInitUser($options['valid']['user']);
fwrite(STDERR, 'FreshRSS exporting ZIP for user “' . $username . "”…\n");
$export_service = new FreshRSS_Export_Service($username);
-$number_entries = empty($options['max-feed-entries']) ? 100 : intval($options['max-feed-entries']);
+$number_entries = empty($options['valid']['max-feed-entries']) ? 100 : intval($options['valid']['max-feed-entries']);
$exported_files = [];
// First, we generate the OPML file
diff --git a/cli/import-for-user.php b/cli/import-for-user.php
index b4731b13b..6969a8946 100755
--- a/cli/import-for-user.php
+++ b/cli/import-for-user.php
@@ -5,20 +5,27 @@ require(__DIR__ . '/_cli.php');
performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? '');
-$params = array(
- 'user:',
- 'filename:',
-);
-
-$options = getopt('', $params);
-
-if (!validateOptions($argv, $params) || empty($options['user']) || empty($options['filename']) || !is_string($options['user']) || !is_string($options['filename'])) {
+$parameters = [
+ 'long' => [
+ 'user' => ':',
+ 'filename' => ':',
+ ],
+ 'short' => [],
+ 'deprecated' => [],
+];
+
+$options = parseCliParams($parameters);
+
+if (!empty($options['invalid'])
+ || empty($options['valid']['user']) || empty($options['valid']['filename'])
+ || !is_string($options['valid']['user']) || !is_string($options['valid']['filename'])
+) {
fail('Usage: ' . basename(__FILE__) . " --user username --filename /path/to/file.ext");
}
-$username = cliInitUser($options['user']);
+$username = cliInitUser($options['valid']['user']);
-$filename = $options['filename'];
+$filename = $options['valid']['filename'];
if (!is_readable($filename)) {
fail('FreshRSS error: file is not readable “' . $filename . '”');
}
diff --git a/cli/import-sqlite-for-user.php b/cli/import-sqlite-for-user.php
index b61a73523..29b7c1b0c 100755
--- a/cli/import-sqlite-for-user.php
+++ b/cli/import-sqlite-for-user.php
@@ -5,20 +5,27 @@ require(__DIR__ . '/_cli.php');
performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? '');
-$params = [
- 'user:',
- 'filename:',
- 'force-overwrite',
+$parameters = [
+ 'long' => [
+ 'user' => ':',
+ 'filename' => ':',
+ 'force-overwrite' => '',
+ ],
+ 'short' => [],
+ 'deprecated' => [],
];
-$options = getopt('', $params);
+$options = parseCliParams($parameters);
-if (!validateOptions($argv, $params) || empty($options['user']) || empty($options['filename']) || !is_string($options['user']) || !is_string($options['filename'])) {
+if (!empty($options['invalid'])
+ || empty($options['valid']['user']) || empty($options['valid']['filename'])
+ || !is_string($options['valid']['user']) || !is_string($options['valid']['filename'])
+) {
fail('Usage: ' . basename(__FILE__) . ' --user username --force-overwrite --filename /path/to/db.sqlite');
}
-$username = cliInitUser($options['user']);
-$filename = $options['filename'];
+$username = cliInitUser($options['valid']['user']);
+$filename = $options['valid']['filename'];
if (pathinfo($filename, PATHINFO_EXTENSION) !== 'sqlite') {
fail('Only *.sqlite files are supported!');
@@ -27,7 +34,7 @@ if (pathinfo($filename, PATHINFO_EXTENSION) !== 'sqlite') {
echo 'FreshRSS importing database from SQLite for user “', $username, "”…\n";
$databaseDAO = FreshRSS_Factory::createDatabaseDAO($username);
-$clearFirst = array_key_exists('force-overwrite', $options);
+$clearFirst = array_key_exists('force-overwrite', $options['valid']);
$ok = $databaseDAO->dbCopy($filename, FreshRSS_DatabaseDAO::SQLITE_IMPORT, $clearFirst);
if (!$ok) {
echo 'If you would like to clear the user database first, use the option --force-overwrite', "\n";
diff --git a/cli/manipulate.translation.php b/cli/manipulate.translation.php
index 611cdb0d5..358a3ec33 100755
--- a/cli/manipulate.translation.php
+++ b/cli/manipulate.translation.php
@@ -1,53 +1,75 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
+require_once __DIR__ . '/_cli.php';
require_once __DIR__ . '/i18n/I18nData.php';
require_once __DIR__ . '/i18n/I18nFile.php';
require_once __DIR__ . '/../constants.php';
-/** @var array<string,string>|false $options */
-$options = getopt('a:hk:l:o:rv:');
-
-if (!is_array($options) || array_key_exists('h', $options)) {
+$parameters = [
+ 'long' => [
+ 'action' => ':',
+ 'help' => '',
+ 'key' => ':',
+ 'language' => ':',
+ 'origin-language' => ':',
+ 'revert' => '',
+ 'value' => ':',
+ ],
+ 'short' => [
+ 'action' => 'a',
+ 'help' => 'h',
+ 'key' => 'k',
+ 'language' => 'l',
+ 'origin-language' => 'o',
+ 'revert' => 'r',
+ 'value' => 'v',
+ ],
+ 'deprecated' => [],
+];
+
+$options = parseCliParams($parameters);
+
+if (!empty($options['invalid']) || array_key_exists('help', $options['valid'])) {
manipulateHelp();
exit();
}
-if (!array_key_exists('a', $options)) {
+if (!array_key_exists('action', $options['valid'])) {
error('You need to specify the action to perform.');
}
$data = new I18nFile();
$i18nData = new I18nData($data->load());
-switch ($options['a']) {
+switch ($options['valid']['action']) {
case 'add' :
- if (array_key_exists('k', $options) && array_key_exists('v', $options) && array_key_exists('l', $options)) {
- $i18nData->addValue($options['k'], $options['v'], $options['l']);
- } elseif (array_key_exists('k', $options) && array_key_exists('v', $options)) {
- $i18nData->addKey($options['k'], $options['v']);
- } elseif (array_key_exists('l', $options)) {
+ if (array_key_exists('key', $options['valid']) && array_key_exists('value', $options['valid']) && array_key_exists('language', $options['valid'])) {
+ $i18nData->addValue($options['valid']['key'], $options['valid']['value'], $options['valid']['language']);
+ } elseif (array_key_exists('key', $options['valid']) && array_key_exists('value', $options['valid'])) {
+ $i18nData->addKey($options['valid']['key'], $options['valid']['value']);
+ } elseif (array_key_exists('language', $options['valid'])) {
$reference = null;
- if (array_key_exists('o', $options)) {
- $reference = $options['o'];
+ if (array_key_exists('origin-language', $options['valid'])) {
+ $reference = $options['valid']['origin-language'];
}
- $i18nData->addLanguage($options['l'], $reference);
+ $i18nData->addLanguage($options['valid']['language'], $reference);
} else {
error('You need to specify a valid set of options.');
exit;
}
break;
case 'delete' :
- if (array_key_exists('k', $options)) {
- $i18nData->removeKey($options['k']);
+ if (array_key_exists('key', $options['valid'])) {
+ $i18nData->removeKey($options['valid']['key']);
} else {
error('You need to specify the key to delete.');
exit;
}
break;
case 'exist':
- if (array_key_exists('k', $options)) {
- $key = $options['k'];
+ if (array_key_exists('key', $options['valid'])) {
+ $key = $options['valid']['key'];
if ($i18nData->isKnown($key)) {
echo "The '{$key}' key is known.\n\n";
} else {
@@ -61,16 +83,16 @@ switch ($options['a']) {
case 'format' :
break;
case 'ignore' :
- if (array_key_exists('l', $options) && array_key_exists('k', $options)) {
- $i18nData->ignore($options['k'], $options['l'], array_key_exists('r', $options));
+ if (array_key_exists('language', $options['valid']) && array_key_exists('key', $options['valid'])) {
+ $i18nData->ignore($options['valid']['key'], $options['valid']['language'], array_key_exists('revert', $options['valid']));
} else {
error('You need to specify a valid set of options.');
exit;
}
break;
case 'ignore_unmodified' :
- if (array_key_exists('l', $options)) {
- $i18nData->ignore_unmodified($options['l'], array_key_exists('r', $options));
+ if (array_key_exists('language', $options['valid'])) {
+ $i18nData->ignore_unmodified($options['valid']['language'], array_key_exists('revert', $options['valid']));
} else {
error('You need to specify a valid set of options.');
exit;
@@ -110,46 +132,48 @@ SYNOPSIS
DESCRIPTION
Manipulate translation files.
- -a=ACTION
- select the action to perform. Available actions are add, delete,
- exist, format, ignore, and ignore_unmodified. This option is mandatory.
- -k=KEY select the key to work on.
- -v=VAL select the value to set.
- -l=LANG select the language to work on.
- -h display this help and exit.
- -r revert the action (only for ignore action)
- -o=LANG select the origin language (only for add language action)
+ -a, --action=ACTION
+ select the action to perform. Available actions are add, delete,
+ exist, format, ignore, and ignore_unmodified. This option is mandatory.
+ -k, --key=KEY select the key to work on.
+ -v, --value=VAL select the value to set.
+ -l, --language=LANG select the language to work on.
+ -h, --help display this help and exit.
+ -r, --revert revert the action (only for ignore action)
+ -o, origin-language=LANG
+ select the origin language (only for add language action)
EXAMPLES
-Example 1: add a language. It adds a new language by duplicating the referential.
+Example 1: add a language. It adds a new language by duplicating the referential.
php $file -a add -l my_lang
php $file -a add -l my_lang -o ref_lang
-Example 2: add a new key. It adds the key for all supported languages.
+Example 2: add a new key. It adds the key for all supported languages.
php $file -a add -k my_key -v my_value
-Example 3: add a new value. It adds a new value for the selected key in the selected language.
+Example 3: add a new value. It adds a new value for the selected key in the selected language.
php $file -a add -k my_key -v my_value -l my_lang
-Example 4: delete a key. It deletes the selected key from all supported languages.
+Example 4: delete a key. It deletes the selected key from all supported languages.
php $file -a delete -k my_key
-Example 5: format i18n files.
+Example 5: format i18n files.
php $file -a format
-Example 6: ignore a key. It adds the key in the ignore file to mark it as translated.
+Example 6: ignore a key. Adds IGNORE comment to the key in the selected language, marking it as translated.
php $file -a ignore -k my_key -l my_lang
-Example 7: revert ignore a key. It removes the key from the ignore file.
+Example 7: revert ignore a key. Removes IGNORE comment from the key in the selected language.
php $file -a ignore -r -k my_key -l my_lang
-Example 8: ignore all unmodified keys. It adds all modified keys in the ignore file to mark it as translated.
+Example 8: ignore all unmodified keys. Adds IGNORE comments to all unmodified keys in the selected language, marking them as translated.
php $file -a ignore_unmodified -l my_lang
-Example 9: revert ignore of all unmodified keys. It removes the unmodified keys from the ignore file. Warning, this will also revert keys added individually.
+Example 9: revert ignore on all unmodified keys. Removes IGNORE comments from all unmodified keys in the selected language.
+ Warning: will also revert individually added unmodified keys.
php $file -a ignore_unmodified -r -l my_lang
-Example 10: check if a key exist.
+Example 10: check if a key exist.
php $file -a exist -k my_key\n\n
HELP;
diff --git a/cli/reconfigure.php b/cli/reconfigure.php
index da433c1a0..fde16c921 100755
--- a/cli/reconfigure.php
+++ b/cli/reconfigure.php
@@ -3,8 +3,8 @@
declare(strict_types=1);
require(__DIR__ . '/_cli.php');
-$parameters = array(
- 'valid' => array(
+$parameters = [
+ 'long' => [
'environment' => ':',
'base-url' => ':',
'language' => ':',
@@ -22,8 +22,9 @@ $parameters = array(
'db-password' => ':',
'db-base' => ':',
'db-prefix' => '::',
- ),
- 'deprecated' => array(
+ ],
+ 'short' => [],
+ 'deprecated' => [
'base-url' => 'base_url',
'default-user' => 'default_user',
'allow-anonymous' => 'allow_anonymous',
@@ -32,10 +33,10 @@ $parameters = array(
'api-enabled' => 'api_enabled',
'allow-robots' => 'allow_robots',
'disable-update' => 'disable_update',
- ),
-);
+ ],
+];
-$configParams = array(
+$configParams = [
'environment',
'base-url',
'language',
@@ -47,16 +48,16 @@ $configParams = array(
'api-enabled',
'allow-robots',
'disable-update',
-);
+];
-$dBconfigParams = array(
+$dBconfigParams = [
'db-type' => 'type',
'db-host' => 'host',
'db-user' => 'user',
'db-password' => 'password',
'db-base' => 'base',
'db-prefix' => 'prefix',
-);
+];
$options = parseCliParams($parameters);
diff --git a/cli/user-info.php b/cli/user-info.php
index fbf60482a..f492d26b2 100755
--- a/cli/user-info.php
+++ b/cli/user-info.php
@@ -5,37 +5,45 @@ require(__DIR__ . '/_cli.php');
const DATA_FORMAT = "%-7s | %-20s | %-5s | %-7s | %-25s | %-15s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-5s | %-10s\n";
-$params = array(
- 'user:',
- 'header',
- 'json',
-);
-$options = getopt('h', $params);
+$parameters = [
+ 'long' => [
+ 'user' => ':',
+ 'header' => '',
+ 'json' => '',
+ 'human-readable' => '',
+ ],
+ 'short' => [
+ 'human-readable' => 'h',
+ ],
+ 'deprecated' => [],
+];
-if (!validateOptions($argv, $params)) {
- fail('Usage: ' . basename(__FILE__) . ' (-h --header --json --user username --user username …)');
+$options = parseCliParams($parameters);
+
+if (!empty($options['invalid'])) {
+ fail('Usage: ' . basename(__FILE__) . ' (--human-readable --header --json --user username --user username …)');
}
-if (empty($options['user'])) {
+if (empty($options['valid']['user'])) {
$users = listUsers();
-} elseif (is_array($options['user'])) {
+} elseif (is_array($options['valid']['user'])) {
/** @var array<string> $users */
- $users = $options['user'];
+ $users = $options['valid']['user'];
} else {
/** @var array<string> $users */
- $users = array($options['user']);
+ $users = [$options['valid']['user']];
}
sort($users);
-$formatJson = isset($options['json']);
+$formatJson = isset($options['valid']['json']);
$jsonOutput = [];
if ($formatJson) {
- unset($options['header']);
- unset($options['h']);
+ unset($options['valid']['header']);
+ unset($options['valid']['human-readable']);
}
-if (array_key_exists('header', $options)) {
+if (array_key_exists('header', $options['valid'])) {
printf(
DATA_FORMAT,
'default',
@@ -84,7 +92,7 @@ foreach ($users as $username) {
'lang' => FreshRSS_Context::userConf()->language,
'mail_login' => FreshRSS_Context::userConf()->mail_login,
);
- if (isset($options['h'])) { //Human format
+ if (isset($options['valid']['human-readable'])) { //Human format
$data['last_user_activity'] = date('c', $data['last_user_activity']);
$data['database_size'] = format_bytes($data['database_size']);
}