Validate configuration parameters on import, and other changes
- Each parameter is checked for type and normalized
- Interval strings are converted to DateInterval objects
- Timeouts can be specified as interval strings
- Most intervals can be null to signify infinity
- Driver classes are checked that they implement the correct interface
- Short driver names may be used, and are used by default
- Helpful errors messages are printed in case of erroneous configuration
Exporting is currently broken; this will be fixed in an upcoming commit
/** Class for loading, saving, and querying configuration
/** Class for loading, saving, and querying configuration
*
*
* The Conf class serves both as a means of importing and querying configuration information, as well as a source for default parameters when a configuration file does not specify a value.
* The Conf class serves both as a means of importing and querying configuration information, as well as a source for default parameters when a configuration file does not specify a value.
@ -15,19 +17,19 @@ class Conf {
/** @var string Default language to use for logging and errors */
/** @var string Default language to use for logging and errors */
public $lang = "en";
public $lang = "en";
/** @var string Class of the database driver in use (SQLite3 by default) */
/** @var string The database driver to use, one of "sqlite3", "postgresql", or "mysql". A fully-qualified class name may also be used for custom drivers */
public $dbDriver = Db\SQLite3\Driver::class;
public $dbDriver = "sqlite3";
/** @var boolean Whether to attempt to automatically update the database when updated to a new version with schema changes */
/** @var boolean Whether to attempt to automatically update the database when upgrading to a new version with schema changes */
public $dbAutoUpdate = true;
public $dbAutoUpdate = true;
/** @var float Number of seconds to wait before returning a timeout error when connecting to a database (zero waits forever; not applicable to SQLite) */
/** @var \DateInterval Number of seconds to wait before returning a timeout error when connecting to a database (zero waits forever; not applicable to SQLite) */
public $dbTimeoutConnect = 5.0;
public $dbTimeoutConnect = 5.0;
/** @var float Number of seconds to wait before returning a timeout error when executing a database operation (zero waits forever; not applicable to SQLite) */
/** @var \DateInterval Number of seconds to wait before returning a timeout error when executing a database operation (zero waits forever; not applicable to SQLite) */
public $dbTimeoutExec = 0.0;
public $dbTimeoutExec = 0.0;
/** @var string|null Full path and file name of SQLite database (if using SQLite) */
/** @var string|null Full path and file name of SQLite database (if using SQLite) */
public $dbSQLite3File = null;
public $dbSQLite3File = null;
/** @var string Encryption key to use for SQLite database (if using a version of SQLite with SEE) */
/** @var string Encryption key to use for SQLite database (if using a version of SQLite with SEE) */
public $dbSQLite3Key = "";
public $dbSQLite3Key = "";
/** @var float Number of seconds for SQLite to wait before returning a timeout error when trying to acquire a write lock on the database (zero does not wait) */
/** @var \DateInterval Number of seconds for SQLite to wait before returning a timeout error when trying to acquire a write lock on the database (zero does not wait) */
public $dbSQLite3Timeout = 60.0;
public $dbSQLite3Timeout = 60.0;
/** @var string Host name, address, or socket path of PostgreSQL database server (if using PostgreSQL) */
/** @var string Host name, address, or socket path of PostgreSQL database server (if using PostgreSQL) */
public $dbPostgreSQLHost = "";
public $dbPostgreSQLHost = "";
@ -43,21 +45,21 @@ class Conf {
public $dbPostgreSQLSchema = "";
public $dbPostgreSQLSchema = "";
/** @var string Service file entry to use (if using PostgreSQL); if using a service entry all above parameters except schema are ignored */
/** @var string Service file entry to use (if using PostgreSQL); if using a service entry all above parameters except schema are ignored */
public $dbPostgreSQLService = "";
public $dbPostgreSQLService = "";
/** @var string Host name, address, or socket path of MySQL/MariaDB database server (if using MySQL/MariaDB) */
/** @var string Host name or address of MySQL database server (if using MySQL) */
public $dbMySQLHost = "localhost";
public $dbMySQLHost = "localhost";
/** @var string Log-in user name for MySQL/MariaDB database server (if using MySQL/MariaDB) */
/** @var string Log-in user name for MySQL database server (if using MySQL) */
public $dbMySQLUser = "arsse";
public $dbMySQLUser = "arsse";
/** @var string Log-in password for MySQL/MariaDB database server (if using MySQL/MariaDB) */
/** @var string Log-in password for MySQL database server (if using MySQL) */
public $dbMySQLPass = "";
public $dbMySQLPass = "";
/** @var integer Listening port for MySQL/MariaDB database server (if using MySQL/MariaDB over TCP) */
/** @var integer Listening port for MySQL database server (if using MySQL over TCP) */
public $dbMySQLPort = 3306;
public $dbMySQLPort = 3306;
/** @var string Database name on MySQL/MariaDB database server (if using MySQL/MariaDB) */
/** @var string Database name on MySQL database server (if using MySQL) */
public $dbMySQLDb = "arsse";
public $dbMySQLDb = "arsse";
/** @var string Unix domain socket or named pipe to use for MySQL when not connecting over TCP */
/** @var string Unix domain socket or named pipe to use for MySQL when not connecting over TCP */
public $dbMySQLSocket = "";
public $dbMySQLSocket = "";
/** @var string Class of the user management driver in use (Internal by default) */
/** @var string The user management driver to use, currently only "internal". A fully-qualified class name may also be used for custom drivers */
public $userDriver = User\Internal\Driver::class;
public $userDriver = "internal";
/** @var boolean Whether users are already authenticated by the Web server before the application is executed */
/** @var boolean Whether users are already authenticated by the Web server before the application is executed */
public $userPreAuth = false;
public $userPreAuth = false;
/** @var boolean Whether to require successful HTTP authentication before processing API-level authentication for protocols which have any. Normally the Tiny Tiny RSS relies on its own session-token authentication scheme, for example */
/** @var boolean Whether to require successful HTTP authentication before processing API-level authentication for protocols which have any. Normally the Tiny Tiny RSS relies on its own session-token authentication scheme, for example */
@ -66,43 +68,43 @@ class Conf {
public $userTempPasswordLength = 20;
public $userTempPasswordLength = 20;
/** @var boolean Whether invalid or expired API session tokens should prevent logging in when HTTP authentication is used, for protocol which implement their own authentication */
/** @var boolean Whether invalid or expired API session tokens should prevent logging in when HTTP authentication is used, for protocol which implement their own authentication */
public $userSessionEnforced = true;
public $userSessionEnforced = true;
/** @var string Period of inactivity after which log-in sessions should be considered invalid, as an ISO 8601 duration (default: 24 hours)
/** @var \DateInterval Period of inactivity after which log-in sessions should be considered invalid, as an ISO 8601 duration (default: 24 hours)
/** @var string Class of the background feed update service driver in use (Forking by default) */
/** @var string Feed update service driver to use, one of "serial", "subprocess", or "curl". A fully-qualified class name may also be used for custom drivers */
public $serviceDriver = Service\Forking\Driver::class;
public $serviceDriver = "subprocess";
/** @var string The interval between checks for new articles, as an ISO 8601 duration
/** @var \DateInterval The interval between checks for new articles, as an ISO 8601 duration
/** @var integer Number of concurrent feed updates to perform */
/** @var integer Number of concurrent feed updates to perform */
public $serviceQueueWidth = 5;
public $serviceQueueWidth = 5;
/** @var string The base server address (with scheme, host, port if necessary, and terminal slash) to connect to the server when performing feed updates using cURL */
/** @var string The base server address (with scheme, host, port if necessary, and terminal slash) to connect to the server when performing feed updates using cURL */
public $serviceCurlBase = "http://localhost/";
public $serviceCurlBase = "http://localhost/";
/** @var string The user name to use when performing feed updates using cURL; if none is provided, a temporary name and password will be stored in the database (this is not compatible with pre-authentication) */
/** @var string The user name to use when performing feed updates using cURL */
public $serviceCurlUser = null;
public $serviceCurlUser = "";
/** @var string The password to use when performing feed updates using cURL */
/** @var string The password to use when performing feed updates using cURL */
public $serviceCurlPassword = null;
public $serviceCurlPassword = "";
/** @var integer Number of seconds to wait for data when fetching feeds from foreign servers */
/** @var \DateInterval Number of seconds to wait for data when fetching feeds from foreign servers */
public $fetchTimeout = 10;
public $fetchTimeout = 10;
/** @var integer Maximum size, in bytes, of data when fetching feeds from foreign servers */
/** @var integer Maximum size, in bytes, of data when fetching feeds from foreign servers */
public $fetchSizeLimit = 2 * 1024 * 1024;
public $fetchSizeLimit = 2 * 1024 * 1024;
/** @var boolean Whether to allow the possibility of fetching full article contents using an item's URL. Whether fetching will actually happen is also governed by a per-feed setting */
/** @var boolean Whether to allow the possibility of fetching full article contents using an item's URL. Whether fetching will actually happen is also governed by a per-feed setting */
public $fetchEnableScraping = true;
public $fetchEnableScraping = true;
/** @var string|null User-Agent string to use when fetching feeds from foreign servers */
/** @var string|null User-Agent string to use when fetching feeds from foreign servers */
public $fetchUserAgentString;
public $fetchUserAgentString = null;
/** @var string When to delete a feed from the database after all its subscriptions have been deleted, as an ISO 8601 duration (default: 24 hours; empty string for never)
/** @var \DateInterval|null When to delete a feed from the database after all its subscriptions have been deleted, as an ISO 8601 duration (default: 24 hours; null for never)
/** @var string When to delete an unstarred article in the database after it has been marked read by all users, as an ISO 8601 duration (default: 7 days; empty string for never)
/** @var \DateInterval|null When to delete an unstarred article in the database after it has been marked read by all users, as an ISO 8601 duration (default: 7 days; null for never)
/** @var string When to delete an unstarred article in the database regardless of its read state, as an ISO 8601 duration (default: 21 days; empty string for never)
/** @var \DateInterval|null When to delete an unstarred article in the database regardless of its read state, as an ISO 8601 duration (default: 21 days; null for never)
/** Layers configuration data from a file into an existing object
/** Layers configuration data from a file into an existing object
*
*
* The file must be a PHP script which return an array with keys that match the properties of the Conf class. Malformed files will throw an exception; unknown keys are silently ignored. Files may be imported is succession, though this is not currently used.
* The file must be a PHP script which returns an array with keys that match the properties of the Conf class. Malformed files will throw an exception; unknown keys are silently accepted. Files may be imported in succession, though this is not currently used.
* @param string $file Full path and file name for the file to import */
* @param string $file Full path and file name for the file to import */
public function importFile(string $file): self {
public function importFile(string $file): self {
if (!file_exists($file)) {
if (!file_exists($file)) {
@ -143,16 +161,22 @@ class Conf {
if (!is_array($arr)) {
if (!is_array($arr)) {
throw new Conf\Exception("fileCorrupt", $file);
throw new Conf\Exception("fileCorrupt", $file);
}
}
return $this->import($arr);
return $this->importData($arr, $file);
}
}
/** Layers configuration data from an associative array into an existing object
/** Layers configuration data from an associative array into an existing object
*
*
* The input array must have keys that match the properties of the Conf class; unknown keys are silently ignored. Arrays may be imported is succession, though this is not currently used.
* The input array must have keys that match the properties of the Conf class; unknown keys are silently accepted. Arrays may be imported in succession, though this is not currently used.
* @param mixed[] $arr Array of configuration parameters to export */
* @param mixed[] $arr Array of configuration parameters to export */
$this->db->query("UPDATE arsse_feeds set orphaned = null where exists(SELECT id from arsse_subscriptions where feed = arsse_feeds.id)");
$this->db->query("UPDATE arsse_feeds set orphaned = null where exists(SELECT id from arsse_subscriptions where feed = arsse_feeds.id)");
// next mark any newly orphaned feeds with the current date and time
// next mark any newly orphaned feeds with the current date and time
$this->db->query("UPDATE arsse_feeds set orphaned = CURRENT_TIMESTAMP where orphaned is null and not exists(SELECT id from arsse_subscriptions where feed = arsse_feeds.id)");
$this->db->query("UPDATE arsse_feeds set orphaned = CURRENT_TIMESTAMP where orphaned is null and not exists(SELECT id from arsse_subscriptions where feed = arsse_feeds.id)");
// finally delete feeds that have been orphaned longer than the retention period
// finally delete feeds that have been orphaned longer than the retention period, if a a purge threshold has been specified
$limit = Date::normalize("now");
if (Arsse::$conf->purgeFeeds) {
if (Arsse::$conf->purgeFeeds) {
// if there is a retention period specified, compute it; otherwise feed are deleted immediatelty
'Configuration parameter "{param}" in file "{file}" must be {type, select,
integer {an integral number}
string {a character string}
boolean {either true or false}
float {a decimal number}
interval {an ISO 8601 time interval}
other {consistent with type "{type}"}
}{nullable, select,
0 {}
other {, or null}
}',
'Exception.JKingWeb/Arsse/Conf/Exception.semanticMismatch' => 'Configuration parameter "{param}" in file "{file}" is not a valid value. Consult the documentation for possible values',
'Exception.JKingWeb/Arsse/Db/Exception.extMissing' => 'Required PHP extension for driver "{0}" not installed',
'Exception.JKingWeb/Arsse/Db/Exception.extMissing' => 'Required PHP extension for driver "{0}" not installed',
'Exception.JKingWeb/Arsse/Db/Exception.fileMissing' => 'Database file "{0}" does not exist',
'Exception.JKingWeb/Arsse/Db/Exception.fileMissing' => 'Database file "{0}" does not exist',
'Exception.JKingWeb/Arsse/Db/Exception.fileUnreadable' => 'Insufficient permissions to open database file "{0}" for reading',
'Exception.JKingWeb/Arsse/Db/Exception.fileUnreadable' => 'Insufficient permissions to open database file "{0}" for reading',
@ -125,7 +83,9 @@ return [
'Exception.JKingWeb/Arsse/Db/Exception.fileUncreatable' => 'Insufficient permissions to create new database file "{0}"',
'Exception.JKingWeb/Arsse/Db/Exception.fileUncreatable' => 'Insufficient permissions to create new database file "{0}"',
'Exception.JKingWeb/Arsse/Db/Exception.fileCorrupt' => 'Database file "{0}" is corrupt or not a valid database',
'Exception.JKingWeb/Arsse/Db/Exception.fileCorrupt' => 'Database file "{0}" is corrupt or not a valid database',
'Exception.JKingWeb/Arsse/Db/Exception.connectionFailure' => 'Could not connect to {engine} database: {message}',
'Exception.JKingWeb/Arsse/Db/Exception.connectionFailure' => 'Could not connect to {engine} database: {message}',
// indicates programming error
'Exception.JKingWeb/Arsse/Db/Exception.paramTypeInvalid' => 'Prepared statement parameter type "{0}" is invalid',
'Exception.JKingWeb/Arsse/Db/Exception.paramTypeInvalid' => 'Prepared statement parameter type "{0}" is invalid',
// indicates programming error
'Exception.JKingWeb/Arsse/Db/Exception.paramTypeUnknown' => 'Prepared statement parameter type "{0}" is valid, but not implemented',
'Exception.JKingWeb/Arsse/Db/Exception.paramTypeUnknown' => 'Prepared statement parameter type "{0}" is valid, but not implemented',
'Exception.JKingWeb/Arsse/Db/Exception.paramTypeMissing' => 'Prepared statement parameter type for parameter #{0} was not specified',
'Exception.JKingWeb/Arsse/Db/Exception.paramTypeMissing' => 'Prepared statement parameter type for parameter #{0} was not specified',