1
1
Fork 0
mirror of https://code.mensbeam.com/MensBeam/Arsse.git synced 2024-12-23 05:44:53 +00:00

Add class constant visibility

This commit is contained in:
J. King 2020-03-01 18:32:01 -05:00
parent bc53a2d24a
commit e60f7ea03f
33 changed files with 150 additions and 140 deletions

View file

@ -25,11 +25,8 @@ $rules = [
], ],
'cast_spaces' => ['space' => "single"], 'cast_spaces' => ['space' => "single"],
'concat_space' => ['spacing' => "none"], 'concat_space' => ['spacing' => "none"],
'declare_equal_normalize' => ['space' => "none"],
'function_typehint_space' => true, 'function_typehint_space' => true,
'list_syntax' => ['syntax' => "short"], 'list_syntax' => ['syntax' => "short"],
'lowercase_cast' => true,
'lowercase_static_reference' => true,
'magic_constant_casing' => true, 'magic_constant_casing' => true,
'magic_method_casing' => true, 'magic_method_casing' => true,
'modernize_types_casting' => true, 'modernize_types_casting' => true,
@ -43,8 +40,6 @@ $rules = [
'no_empty_phpdoc' => true, 'no_empty_phpdoc' => true,
'no_empty_statement' => true, 'no_empty_statement' => true,
'no_extra_blank_lines' => true, // this could probably use more configuration 'no_extra_blank_lines' => true, // this could probably use more configuration
'no_leading_import_slash' => true,
'no_leading_namespace_whitespace' => true,
'no_mixed_echo_print' => ['use' => "echo"], 'no_mixed_echo_print' => ['use' => "echo"],
'no_short_bool_cast' => true, 'no_short_bool_cast' => true,
'no_trailing_comma_in_singleline_array' => true, 'no_trailing_comma_in_singleline_array' => true,
@ -58,13 +53,20 @@ $rules = [
'pow_to_exponentiation' => true, 'pow_to_exponentiation' => true,
'return_type_declaration' => ['space_before' => "none"], 'return_type_declaration' => ['space_before' => "none"],
'set_type_to_cast' => true, 'set_type_to_cast' => true,
'short_scalar_cast' => true,
'standardize_not_equals' => true, 'standardize_not_equals' => true,
'trailing_comma_in_multiline_array' => true, 'trailing_comma_in_multiline_array' => true,
'unary_operator_spaces' => true, 'unary_operator_spaces' => true,
'yoda_style' => false, 'yoda_style' => false,
// PSR standard to apply // PSR standard to apply
'@PSR2' => true, '@PSR2' => true,
// PSR-12 rules; php-cs-fixer does not yet support PSR-12 natively
'declare_equal_normalize' => ['space' => "none"],
'lowercase_cast' => true,
'lowercase_static_reference' => true,
'no_leading_import_slash' => true,
'no_leading_namespace_whitespace' => true,
'short_scalar_cast' => true,
'visibility_required' => ['elements' => ["const", "property", "method"]],
// house exceptions to PSR rules // house exceptions to PSR rules
'braces' => ['position_after_functions_and_oop_constructs' => "same"], 'braces' => ['position_after_functions_and_oop_constructs' => "same"],
'function_declaration' => ['closure_function_spacing' => "none"], 'function_declaration' => ['closure_function_spacing' => "none"],

View file

@ -7,7 +7,7 @@ declare(strict_types=1);
namespace JKingWeb\Arsse; namespace JKingWeb\Arsse;
abstract class AbstractException extends \Exception { abstract class AbstractException extends \Exception {
const CODES = [ public const CODES = [
"Exception.uncoded" => -1, "Exception.uncoded" => -1,
"Exception.unknown" => 10000, "Exception.unknown" => 10000,
"Exception.constantUnknown" => 10001, "Exception.constantUnknown" => 10001,

View file

@ -7,7 +7,7 @@ declare(strict_types=1);
namespace JKingWeb\Arsse; namespace JKingWeb\Arsse;
class Arsse { class Arsse {
const VERSION = "0.8.3"; public const VERSION = "0.8.3";
/** @var Lang */ /** @var Lang */
public static $lang; public static $lang;

View file

@ -10,7 +10,7 @@ use JKingWeb\Arsse\REST\Fever\User as Fever;
use JKingWeb\Arsse\ImportExport\OPML; use JKingWeb\Arsse\ImportExport\OPML;
class CLI { class CLI {
const USAGE = <<<USAGE_TEXT public const USAGE = <<<USAGE_TEXT
Usage: Usage:
arsse.php daemon arsse.php daemon
arsse.php feed refresh-all arsse.php feed refresh-all

View file

@ -114,14 +114,14 @@ class Conf {
/** @var \DateInterval|null (OBSOLETE) 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|null (OBSOLETE) 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 = null; // previously 60.0 public $dbSQLite3Timeout = null; // previously 60.0
const TYPE_NAMES = [ protected const TYPE_NAMES = [
Value::T_BOOL => "boolean", Value::T_BOOL => "boolean",
Value::T_STRING => "string", Value::T_STRING => "string",
Value::T_FLOAT => "float", Value::T_FLOAT => "float",
VALUE::T_INT => "integer", VALUE::T_INT => "integer",
Value::T_INTERVAL => "interval", Value::T_INTERVAL => "interval",
]; ];
const EXPECTED_TYPES = [ protected const EXPECTED_TYPES = [
'dbTimeoutExec' => "double", 'dbTimeoutExec' => "double",
'dbTimeoutLock' => "double", 'dbTimeoutLock' => "double",
'dbTimeoutConnect' => "double", 'dbTimeoutConnect' => "double",

View file

@ -39,23 +39,23 @@ use JKingWeb\Arsse\Misc\URL;
*/ */
class Database { class Database {
/** The version number of the latest schema the interface is aware of */ /** The version number of the latest schema the interface is aware of */
const SCHEMA_VERSION = 6; public const SCHEMA_VERSION = 6;
/** The size of a set of values beyond which the set will be embedded into the query text */
const LIMIT_SET_SIZE = 25;
/** The length of a string in an embedded set beyond which a parameter placeholder will be used for the string */
const LIMIT_SET_STRING_LENGTH = 200;
/** Makes tag/label association change operations remove members */ /** Makes tag/label association change operations remove members */
const ASSOC_REMOVE = 0; public const ASSOC_REMOVE = 0;
/** Makes tag/label association change operations add members */ /** Makes tag/label association change operations add members */
const ASSOC_ADD = 1; public const ASSOC_ADD = 1;
/** Makes tag/label association change operations replace members */ /** Makes tag/label association change operations replace members */
const ASSOC_REPLACE = 2; public const ASSOC_REPLACE = 2;
/** A map of database driver short-names and their associated class names */ /** A map of database driver short-names and their associated class names */
const DRIVER_NAMES = [ public const DRIVER_NAMES = [
'sqlite3' => \JKingWeb\Arsse\Db\SQLite3\Driver::class, 'sqlite3' => \JKingWeb\Arsse\Db\SQLite3\Driver::class,
'postgresql' => \JKingWeb\Arsse\Db\PostgreSQL\Driver::class, 'postgresql' => \JKingWeb\Arsse\Db\PostgreSQL\Driver::class,
'mysql' => \JKingWeb\Arsse\Db\MySQL\Driver::class, 'mysql' => \JKingWeb\Arsse\Db\MySQL\Driver::class,
]; ];
/** The size of a set of values beyond which the set will be embedded into the query text */
protected const LIMIT_SET_SIZE = 25;
/** The length of a string in an embedded set beyond which a parameter placeholder will be used for the string */
protected const LIMIT_SET_STRING_LENGTH = 200;
/** @var Db\Driver */ /** @var Db\Driver */
public $db; public $db;

View file

@ -12,7 +12,7 @@ use JKingWeb\Arsse\Misc\ValueInfo;
abstract class AbstractStatement implements Statement { abstract class AbstractStatement implements Statement {
use SQLState; use SQLState;
const TYPE_NORM_MAP = [ public const TYPE_NORM_MAP = [
self::T_INTEGER => ValueInfo::M_NULL | ValueInfo::T_INT, self::T_INTEGER => ValueInfo::M_NULL | ValueInfo::T_INT,
self::T_STRING => ValueInfo::M_NULL | ValueInfo::T_STRING, self::T_STRING => ValueInfo::M_NULL | ValueInfo::T_STRING,
self::T_BOOLEAN => ValueInfo::M_NULL | ValueInfo::T_BOOL, self::T_BOOLEAN => ValueInfo::M_NULL | ValueInfo::T_BOOL,

View file

@ -7,11 +7,11 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\Db; namespace JKingWeb\Arsse\Db;
interface Driver { interface Driver {
const TR_PEND = 0; public const TR_PEND = 0;
const TR_COMMIT = 1; public const TR_COMMIT = 1;
const TR_ROLLBACK = 2; public const TR_ROLLBACK = 2;
const TR_PEND_COMMIT = -1; public const TR_PEND_COMMIT = -1;
const TR_PEND_ROLLBACK = -2; public const TR_PEND_ROLLBACK = -2;
/** Creates and returns an instance of the class; this is so that either a native or PDO driver may be returned depending on what is available on the server */ /** Creates and returns an instance of the class; this is so that either a native or PDO driver may be returned depending on what is available on the server */
public static function create(): Driver; public static function create(): Driver;

View file

@ -12,8 +12,8 @@ use JKingWeb\Arsse\Db\Exception;
class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
use ExceptionBuilder; use ExceptionBuilder;
const SQL_MODE = "ANSI_QUOTES,HIGH_NOT_PRECEDENCE,NO_BACKSLASH_ESCAPES,NO_ENGINE_SUBSTITUTION,PIPES_AS_CONCAT,STRICT_ALL_TABLES"; protected const SQL_MODE = "ANSI_QUOTES,HIGH_NOT_PRECEDENCE,NO_BACKSLASH_ESCAPES,NO_ENGINE_SUBSTITUTION,PIPES_AS_CONCAT,STRICT_ALL_TABLES";
const TRANSACTIONAL_LOCKS = false; protected const TRANSACTIONAL_LOCKS = false;
/** @var \mysqli */ /** @var \mysqli */
protected $db; protected $db;

View file

@ -9,7 +9,7 @@ namespace JKingWeb\Arsse\Db\MySQL;
class Statement extends \JKingWeb\Arsse\Db\AbstractStatement { class Statement extends \JKingWeb\Arsse\Db\AbstractStatement {
use ExceptionBuilder; use ExceptionBuilder;
const BINDINGS = [ protected const BINDINGS = [
self::T_INTEGER => "i", self::T_INTEGER => "i",
self::T_FLOAT => "d", self::T_FLOAT => "d",
self::T_DATETIME => "s", self::T_DATETIME => "s",

View file

@ -9,7 +9,7 @@ namespace JKingWeb\Arsse\Db;
abstract class PDOStatement extends AbstractStatement { abstract class PDOStatement extends AbstractStatement {
use PDOError; use PDOError;
const BINDINGS = [ protected const BINDINGS = [
self::T_INTEGER => \PDO::PARAM_INT, self::T_INTEGER => \PDO::PARAM_INT,
self::T_FLOAT => \PDO::PARAM_STR, self::T_FLOAT => \PDO::PARAM_STR,
self::T_DATETIME => \PDO::PARAM_STR, self::T_DATETIME => \PDO::PARAM_STR,

View file

@ -12,7 +12,7 @@ use JKingWeb\Arsse\Db\Exception;
class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
use Dispatch; use Dispatch;
const TRANSACTIONAL_LOCKS = true; protected const TRANSACTIONAL_LOCKS = true;
protected $db; protected $db;
protected $transStart = 0; protected $transStart = 0;

View file

@ -9,7 +9,7 @@ namespace JKingWeb\Arsse\Db\PostgreSQL;
class Statement extends \JKingWeb\Arsse\Db\AbstractStatement { class Statement extends \JKingWeb\Arsse\Db\AbstractStatement {
use Dispatch; use Dispatch;
const BINDINGS = [ protected const BINDINGS = [
self::T_INTEGER => "bigint", self::T_INTEGER => "bigint",
self::T_FLOAT => "decimal", self::T_FLOAT => "decimal",
self::T_DATETIME => "timestamp(0) without time zone", self::T_DATETIME => "timestamp(0) without time zone",

View file

@ -12,12 +12,12 @@ use JKingWeb\Arsse\Db\Exception;
class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
use ExceptionBuilder; use ExceptionBuilder;
const TRANSACTIONAL_LOCKS = true; protected const TRANSACTIONAL_LOCKS = true;
const SQLITE_BUSY = 5; public const SQLITE_BUSY = 5;
const SQLITE_SCHEMA = 17; public const SQLITE_SCHEMA = 17;
const SQLITE_CONSTRAINT = 19; public const SQLITE_CONSTRAINT = 19;
const SQLITE_MISMATCH = 20; public const SQLITE_MISMATCH = 20;
protected $db; protected $db;

View file

@ -9,10 +9,10 @@ namespace JKingWeb\Arsse\Db\SQLite3;
class Statement extends \JKingWeb\Arsse\Db\AbstractStatement { class Statement extends \JKingWeb\Arsse\Db\AbstractStatement {
use ExceptionBuilder; use ExceptionBuilder;
const SQLITE_BUSY = 5; public const SQLITE_BUSY = 5;
const SQLITE_CONSTRAINT = 19; public const SQLITE_CONSTRAINT = 19;
const SQLITE_MISMATCH = 20; public const SQLITE_MISMATCH = 20;
const BINDINGS = [ protected const BINDINGS = [
self::T_INTEGER => \SQLITE3_INTEGER, self::T_INTEGER => \SQLITE3_INTEGER,
self::T_FLOAT => \SQLITE3_FLOAT, self::T_FLOAT => \SQLITE3_FLOAT,
self::T_DATETIME => \SQLITE3_TEXT, self::T_DATETIME => \SQLITE3_TEXT,

View file

@ -7,7 +7,7 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\Db; namespace JKingWeb\Arsse\Db;
interface Statement { interface Statement {
const TYPES = [ public const TYPES = [
'int' => self::T_INTEGER, 'int' => self::T_INTEGER,
'integer' => self::T_INTEGER, 'integer' => self::T_INTEGER,
'float' => self::T_FLOAT, 'float' => self::T_FLOAT,
@ -43,13 +43,13 @@ interface Statement {
'strict boolean' => self::T_NOT_NULL + self::T_BOOLEAN, 'strict boolean' => self::T_NOT_NULL + self::T_BOOLEAN,
'strict bit' => self::T_NOT_NULL + self::T_BOOLEAN, 'strict bit' => self::T_NOT_NULL + self::T_BOOLEAN,
]; ];
const T_INTEGER = 1; public const T_INTEGER = 1;
const T_STRING = 2; public const T_STRING = 2;
const T_BOOLEAN = 3; public const T_BOOLEAN = 3;
const T_DATETIME = 4; public const T_DATETIME = 4;
const T_FLOAT = 5; public const T_FLOAT = 5;
const T_BINARY = 6; public const T_BINARY = 6;
const T_NOT_NULL = 100; public const T_NOT_NULL = 100;
public function run(...$values): Result; public function run(...$values): Result;
public function runArray(array $values = []): Result; public function runArray(array $values = []): Result;

View file

@ -12,8 +12,8 @@ use GuzzleHttp\Exception\TooManyRedirectsException;
use PicoFeed\PicoFeedException; use PicoFeed\PicoFeedException;
class Exception extends \JKingWeb\Arsse\AbstractException { class Exception extends \JKingWeb\Arsse\AbstractException {
const CURL_ERROR_MAP = [1 => "invalidUrl",3 => "invalidUrl",5 => "transmissionError","connectionFailed","connectionFailed","transmissionError","forbidden","unauthorized","transmissionError","transmissionError","transmissionError","transmissionError","connectionFailed","connectionFailed","transmissionError","transmissionError","transmissionError","transmissionError","transmissionError","invalidUrl","transmissionError","transmissionError","transmissionError","transmissionError",28 => "timeout","transmissionError","transmissionError","transmissionError","transmissionError","transmissionError",35 => "invalidCertificate","transmissionError","transmissionError","transmissionError","transmissionError",45 => "transmissionError","unauthorized","maxRedirect",52 => "transmissionError","invalidCertificate","invalidCertificate","transmissionError","transmissionError",58 => "invalidCertificate","invalidCertificate","invalidCertificate","transmissionError","invalidUrl","transmissionError","invalidCertificate","transmissionError","invalidCertificate","forbidden","invalidUrl","forbidden","transmissionError",73 => "transmissionError","transmissionError",77 => "invalidCertificate","invalidUrl",90 => "invalidCertificate","invalidCertificate","transmissionError",94 => "unauthorized","transmissionError","connectionFailed"]; protected const CURL_ERROR_MAP = [1 => "invalidUrl",3 => "invalidUrl",5 => "transmissionError","connectionFailed","connectionFailed","transmissionError","forbidden","unauthorized","transmissionError","transmissionError","transmissionError","transmissionError","connectionFailed","connectionFailed","transmissionError","transmissionError","transmissionError","transmissionError","transmissionError","invalidUrl","transmissionError","transmissionError","transmissionError","transmissionError",28 => "timeout","transmissionError","transmissionError","transmissionError","transmissionError","transmissionError",35 => "invalidCertificate","transmissionError","transmissionError","transmissionError","transmissionError",45 => "transmissionError","unauthorized","maxRedirect",52 => "transmissionError","invalidCertificate","invalidCertificate","transmissionError","transmissionError",58 => "invalidCertificate","invalidCertificate","invalidCertificate","transmissionError","invalidUrl","transmissionError","invalidCertificate","transmissionError","invalidCertificate","forbidden","invalidUrl","forbidden","transmissionError",73 => "transmissionError","transmissionError",77 => "invalidCertificate","invalidUrl",90 => "invalidCertificate","invalidCertificate","transmissionError",94 => "unauthorized","transmissionError","connectionFailed"];
const HTTP_ERROR_MAP = [401 => "unauthorized",403 => "forbidden",404 => "invalidUrl",408 => "timeout",410 => "invalidUrl",414 => "invalidUrl",451 => "invalidUrl"]; protected const HTTP_ERROR_MAP = [401 => "unauthorized",403 => "forbidden",404 => "invalidUrl",408 => "timeout",410 => "invalidUrl",414 => "invalidUrl",451 => "invalidUrl"];
public function __construct($url, \Throwable $e) { public function __construct($url, \Throwable $e) {
if ($e instanceof BadResponseException) { if ($e instanceof BadResponseException) {

View file

@ -7,8 +7,8 @@ declare(strict_types=1);
namespace JKingWeb\Arsse; namespace JKingWeb\Arsse;
class Lang { class Lang {
const DEFAULT = "en"; // fallback locale public const DEFAULT = "en"; // fallback locale
const REQUIRED = [ // collection of absolutely required strings to handle pathological errors protected const REQUIRED = [ // collection of absolutely required strings to handle pathological errors
'Exception.JKingWeb/Arsse/Exception.uncoded' => 'The specified exception symbol {0} has no code specified in AbstractException.php', 'Exception.JKingWeb/Arsse/Exception.uncoded' => 'The specified exception symbol {0} has no code specified in AbstractException.php',
'Exception.JKingWeb/Arsse/Exception.unknown' => 'An unknown error has occurred', 'Exception.JKingWeb/Arsse/Exception.unknown' => 'An unknown error has occurred',
'Exception.JKingWeb/Arsse/Lang/Exception.defaultFileMissing' => 'Default language file "{0}" missing', 'Exception.JKingWeb/Arsse/Lang/Exception.defaultFileMissing' => 'Default language file "{0}" missing',

View file

@ -10,33 +10,33 @@ use JKingWeb\Arsse\ExceptionType;
class ValueInfo { class ValueInfo {
// universal // universal
const VALID = 1 << 0; public const VALID = 1 << 0;
const NULL = 1 << 1; public const NULL = 1 << 1;
// integers // integers
const ZERO = 1 << 2; public const ZERO = 1 << 2;
const NEG = 1 << 3; public const NEG = 1 << 3;
const FLOAT = 1 << 4; public const FLOAT = 1 << 4;
// strings // strings
const EMPTY = 1 << 2; public const EMPTY = 1 << 2;
const WHITE = 1 << 3; public const WHITE = 1 << 3;
// normalization types // normalization types
const T_MIXED = 0; // pass through unchanged public const T_MIXED = 0; // pass through unchanged
const T_NULL = 1; // convert to null public const T_NULL = 1; // convert to null
const T_BOOL = 2; // convert to boolean public const T_BOOL = 2; // convert to boolean
const T_INT = 3; // convert to integer public const T_INT = 3; // convert to integer
const T_FLOAT = 4; // convert to floating point public const T_FLOAT = 4; // convert to floating point
const T_DATE = 5; // convert to DateTimeInterface instance public const T_DATE = 5; // convert to DateTimeInterface instance
const T_STRING = 6; // convert to string public const T_STRING = 6; // convert to string
const T_ARRAY = 7; // convert to array public const T_ARRAY = 7; // convert to array
const T_INTERVAL = 8; // convert to time interval public const T_INTERVAL = 8; // convert to time interval
// normalization modes // normalization modes
const M_LOOSE = 0; public const M_LOOSE = 0;
const M_NULL = 1 << 28; // pass nulls through regardless of target type public const M_NULL = 1 << 28; // pass nulls through regardless of target type
const M_DROP = 1 << 29; // drop the value (return null) if the type doesn't match public const M_DROP = 1 << 29; // drop the value (return null) if the type doesn't match
const M_STRICT = 1 << 30; // throw an exception if the type doesn't match public const M_STRICT = 1 << 30; // throw an exception if the type doesn't match
const M_ARRAY = 1 << 31; // the value should be a flat array of values of the specified type; indexed and associative are both acceptable public const M_ARRAY = 1 << 31; // the value should be a flat array of values of the specified type; indexed and associative are both acceptable
// symbolic date and time formats // symbolic date and time formats
const DATE_FORMATS = [ // in out protected const DATE_FORMATS = [ // in out
'iso8601' => ["!Y-m-d\TH:i:s", "Y-m-d\TH:i:s\Z" ], // NOTE: ISO 8601 dates require special input processing because of varying formats for timezone offsets 'iso8601' => ["!Y-m-d\TH:i:s", "Y-m-d\TH:i:s\Z" ], // NOTE: ISO 8601 dates require special input processing because of varying formats for timezone offsets
'iso8601m' => ["!Y-m-d\TH:i:s.u", "Y-m-d\TH:i:s.u\Z" ], // NOTE: ISO 8601 dates require special input processing because of varying formats for timezone offsets 'iso8601m' => ["!Y-m-d\TH:i:s.u", "Y-m-d\TH:i:s.u\Z" ], // NOTE: ISO 8601 dates require special input processing because of varying formats for timezone offsets
'microtime' => ["U.u", "0.u00 U" ], // NOTE: the actual input format at the user level matches the output format; pre-processing is required for PHP not to fail 'microtime' => ["U.u", "0.u00 U" ], // NOTE: the actual input format at the user level matches the output format; pre-processing is required for PHP not to fail

View file

@ -14,7 +14,7 @@ use Laminas\Diactoros\ServerRequestFactory;
use Laminas\Diactoros\Response\EmptyResponse; use Laminas\Diactoros\Response\EmptyResponse;
class REST { class REST {
const API_LIST = [ public const API_LIST = [
'ncn' => [ // Nextcloud News version enumerator 'ncn' => [ // Nextcloud News version enumerator
'match' => '/index.php/apps/news/api', 'match' => '/index.php/apps/news/api',
'strip' => '/index.php/apps/news/api', 'strip' => '/index.php/apps/news/api',
@ -55,7 +55,7 @@ class REST {
// Proprietary (centralized) entities: // Proprietary (centralized) entities:
// Feedly https://developer.feedly.com/ // Feedly https://developer.feedly.com/
]; ];
const DEFAULT_PORTS = [ protected const DEFAULT_PORTS = [
'http' => 80, 'http' => 80,
'https' => 443, 'https' => 443,
]; ];

View file

@ -19,15 +19,15 @@ use Laminas\Diactoros\Response\XmlResponse;
use Laminas\Diactoros\Response\EmptyResponse; use Laminas\Diactoros\Response\EmptyResponse;
class API extends \JKingWeb\Arsse\REST\AbstractHandler { class API extends \JKingWeb\Arsse\REST\AbstractHandler {
const LEVEL = 3; public const LEVEL = 3;
const GENERIC_ICON_TYPE = "image/png;base64"; protected const GENERIC_ICON_TYPE = "image/png;base64";
const GENERIC_ICON_DATA = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAAADUlEQVQYV2NgYGBgAAAABQABijPjAAAAAABJRU5ErkJggg=="; protected const GENERIC_ICON_DATA = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAAADUlEQVQYV2NgYGBgAAAABQABijPjAAAAAABJRU5ErkJggg==";
const ACCEPTED_TYPE = "application/x-www-form-urlencoded"; protected const ACCEPTED_TYPE = "application/x-www-form-urlencoded";
// GET parameters for which we only check presence: these will be converted to booleans // GET parameters for which we only check presence: these will be converted to booleans
const PARAM_BOOL = ["groups", "feeds", "items", "favicons", "links", "unread_item_ids", "saved_item_ids"]; protected const PARAM_BOOL = ["groups", "feeds", "items", "favicons", "links", "unread_item_ids", "saved_item_ids"];
// GET parameters which contain meaningful values // GET parameters which contain meaningful values
const PARAM_GET = [ protected const PARAM_GET = [
'api' => V::T_STRING, // this parameter requires special handling 'api' => V::T_STRING, // this parameter requires special handling
'page' => V::T_INT, // parameter for hot links 'page' => V::T_INT, // parameter for hot links
'range' => V::T_INT, // parameter for hot links 'range' => V::T_INT, // parameter for hot links
@ -45,7 +45,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
'unread_recently_read' => V::T_BOOL, 'unread_recently_read' => V::T_BOOL,
]; ];
// POST parameters, all of which contain meaningful values // POST parameters, all of which contain meaningful values
const PARAM_POST = [ protected const PARAM_POST = [
'api_key' => V::T_STRING, 'api_key' => V::T_STRING,
'mark' => V::T_STRING, 'mark' => V::T_STRING,
'as' => V::T_STRING, 'as' => V::T_STRING,

View file

@ -23,9 +23,9 @@ use Laminas\Diactoros\Response\JsonResponse as Response;
use Laminas\Diactoros\Response\EmptyResponse; use Laminas\Diactoros\Response\EmptyResponse;
class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
const REALM = "Nextcloud News API v1-2"; public const VERSION = "11.0.5";
const VERSION = "11.0.5"; protected const REALM = "Nextcloud News API v1-2";
const ACCEPTED_TYPE = "application/json"; protected const ACCEPTED_TYPE = "application/json";
protected $dateFormat = "unix"; protected $dateFormat = "unix";

View file

@ -24,27 +24,27 @@ use Laminas\Diactoros\Response\JsonResponse as Response;
use Laminas\Diactoros\Response\EmptyResponse; use Laminas\Diactoros\Response\EmptyResponse;
class API extends \JKingWeb\Arsse\REST\AbstractHandler { class API extends \JKingWeb\Arsse\REST\AbstractHandler {
const LEVEL = 14; // emulated API level public const LEVEL = 14; // emulated API level
const VERSION = "17.4"; // emulated TT-RSS version public const VERSION = "17.4"; // emulated TT-RSS version
const LABEL_OFFSET = 1024; // offset below zero at which labels begin, counting down protected const LABEL_OFFSET = 1024; // offset below zero at which labels begin, counting down
const LIMIT_ARTICLES = 200; // maximum number of articles returned by getHeadlines protected const LIMIT_ARTICLES = 200; // maximum number of articles returned by getHeadlines
const LIMIT_EXCERPT = 100; // maximum length of excerpts in getHeadlines, counted in grapheme units protected const LIMIT_EXCERPT = 100; // maximum length of excerpts in getHeadlines, counted in grapheme units
// special feeds // special feeds
const FEED_ARCHIVED = 0; protected const FEED_ARCHIVED = 0;
const FEED_STARRED = -1; protected const FEED_STARRED = -1;
const FEED_PUBLISHED = -2; protected const FEED_PUBLISHED = -2;
const FEED_FRESH = -3; protected const FEED_FRESH = -3;
const FEED_ALL = -4; protected const FEED_ALL = -4;
const FEED_READ = -6; protected const FEED_READ = -6;
// special categories // special categories
const CAT_UNCATEGORIZED = 0; protected const CAT_UNCATEGORIZED = 0;
const CAT_SPECIAL = -1; protected const CAT_SPECIAL = -1;
const CAT_LABELS = -2; protected const CAT_LABELS = -2;
const CAT_NOT_SPECIAL = -3; protected const CAT_NOT_SPECIAL = -3;
const CAT_ALL = -4; protected const CAT_ALL = -4;
// valid input // valid input
const ACCEPTED_TYPES = ["application/json", "text/json"]; protected const ACCEPTED_TYPES = ["application/json", "text/json"];
const VALID_INPUT = [ protected const VALID_INPUT = [
'op' => ValueInfo::T_STRING, // the function ("operation") to perform 'op' => ValueInfo::T_STRING, // the function ("operation") to perform
'sid' => ValueInfo::T_STRING, // session ID 'sid' => ValueInfo::T_STRING, // session ID
'seq' => ValueInfo::T_INT, // request number from client 'seq' => ValueInfo::T_INT, // request number from client
@ -82,7 +82,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
'data' => ValueInfo::T_STRING, // note text in `updateArticle` if setting a note 'data' => ValueInfo::T_STRING, // note text in `updateArticle` if setting a note
]; ];
// generic error construct // generic error construct
const FATAL_ERR = [ protected const FATAL_ERR = [
'seq' => null, 'seq' => null,
'status' => 1, 'status' => 1,
'content' => ['error' => "MALFORMED_INPUT"], 'content' => ['error' => "MALFORMED_INPUT"],

View file

@ -10,22 +10,22 @@ use JKingWeb\Arsse\Context\Context;
use JKingWeb\Arsse\Misc\Date; use JKingWeb\Arsse\Misc\Date;
class Search { class Search {
const STATE_BEFORE_TOKEN = 0; protected const STATE_BEFORE_TOKEN = 0;
const STATE_BEFORE_TOKEN_QUOTED = 1; protected const STATE_BEFORE_TOKEN_QUOTED = 1;
const STATE_IN_DATE = 2; protected const STATE_IN_DATE = 2;
const STATE_IN_DATE_QUOTED = 3; protected const STATE_IN_DATE_QUOTED = 3;
const STATE_IN_TOKEN_OR_TAG = 4; protected const STATE_IN_TOKEN_OR_TAG = 4;
const STATE_IN_TOKEN_OR_TAG_QUOTED = 5; protected const STATE_IN_TOKEN_OR_TAG_QUOTED = 5;
const STATE_IN_TOKEN = 6; protected const STATE_IN_TOKEN = 6;
const STATE_IN_TOKEN_QUOTED = 7; protected const STATE_IN_TOKEN_QUOTED = 7;
const FIELDS_BOOLEAN = [ protected const FIELDS_BOOLEAN = [
"unread" => "unread", "unread" => "unread",
"star" => "starred", "star" => "starred",
"note" => "annotated", "note" => "annotated",
"pub" => "published", // TODO: not implemented "pub" => "published", // TODO: not implemented
]; ];
const FIELDS_TEXT = [ protected const FIELDS_TEXT = [
"title" => "titleTerms", "title" => "titleTerms",
"author" => "authorTerms", "author" => "authorTerms",
"note" => "annotationTerms", "note" => "annotationTerms",
@ -36,7 +36,6 @@ class Search {
// normalize the input // normalize the input
$search = strtolower(trim(preg_replace("<\s+>", " ", $search))); $search = strtolower(trim(preg_replace("<\s+>", " ", $search)));
// set initial state // set initial state
$tokens = [];
$pos = -1; $pos = -1;
$stop = strlen($search); $stop = strlen($search);
$state = self::STATE_BEFORE_TOKEN; $state = self::STATE_BEFORE_TOKEN;

View file

@ -9,7 +9,7 @@ namespace JKingWeb\Arsse;
use JKingWeb\Arsse\Misc\Date; use JKingWeb\Arsse\Misc\Date;
class Service { class Service {
const DRIVER_NAMES = [ public const DRIVER_NAMES = [
'serial' => \JKingWeb\Arsse\Service\Serial\Driver::class, 'serial' => \JKingWeb\Arsse\Service\Serial\Driver::class,
'subprocess' => \JKingWeb\Arsse\Service\Subprocess\Driver::class, 'subprocess' => \JKingWeb\Arsse\Service\Subprocess\Driver::class,
]; ];

View file

@ -9,7 +9,7 @@ namespace JKingWeb\Arsse;
use PasswordGenerator\Generator as PassGen; use PasswordGenerator\Generator as PassGen;
class User { class User {
const DRIVER_NAMES = [ public const DRIVER_NAMES = [
'internal' => \JKingWeb\Arsse\User\Internal\Driver::class, 'internal' => \JKingWeb\Arsse\User\Internal\Driver::class,
]; ];

View file

@ -7,9 +7,9 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\User; namespace JKingWeb\Arsse\User;
interface Driver { interface Driver {
const FUNC_NOT_IMPLEMENTED = 0; public const FUNC_NOT_IMPLEMENTED = 0;
const FUNC_INTERNAL = 1; public const FUNC_INTERNAL = 1;
const FUNC_EXTERNAL = 2; public const FUNC_EXTERNAL = 2;
// returns an instance of a class implementing this interface. // returns an instance of a class implementing this interface.
public function __construct(); public function __construct();

View file

@ -421,6 +421,7 @@ trait SeriesArticle {
} }
public function provideContextMatches(): iterable { public function provideContextMatches(): iterable {
$setSize = (new \ReflectionClassConstant(Database::class, "LIMIT_SET_SIZE"))->getValue();
return [ return [
'Blank context' => [new Context, [1,2,3,4,5,6,7,8,19,20]], 'Blank context' => [new Context, [1,2,3,4,5,6,7,8,19,20]],
'Folder tree' => [(new Context)->folder(1), [5,6,7,8]], 'Folder tree' => [(new Context)->folder(1), [5,6,7,8]],
@ -473,7 +474,7 @@ trait SeriesArticle {
'Multiple unstarred articles' => [(new Context)->articles([1,2,3])->starred(false), [2,3]], 'Multiple unstarred articles' => [(new Context)->articles([1,2,3])->starred(false), [2,3]],
'Multiple articles' => [(new Context)->articles([1,20,50]), [1,20]], 'Multiple articles' => [(new Context)->articles([1,20,50]), [1,20]],
'Multiple editions' => [(new Context)->editions([1,1001,50]), [1,20]], 'Multiple editions' => [(new Context)->editions([1,1001,50]), [1,20]],
'150 articles' => [(new Context)->articles(range(1, Database::LIMIT_SET_SIZE * 3)), [1,2,3,4,5,6,7,8,19,20]], '150 articles' => [(new Context)->articles(range(1, $setSize * 3)), [1,2,3,4,5,6,7,8,19,20]],
'Search title or content 1' => [(new Context)->searchTerms(["Article"]), [1,2,3]], 'Search title or content 1' => [(new Context)->searchTerms(["Article"]), [1,2,3]],
'Search title or content 2' => [(new Context)->searchTerms(["one", "first"]), [1]], 'Search title or content 2' => [(new Context)->searchTerms(["one", "first"]), [1]],
'Search title or content 3' => [(new Context)->searchTerms(["one first"]), []], 'Search title or content 3' => [(new Context)->searchTerms(["one first"]), []],
@ -816,7 +817,8 @@ trait SeriesArticle {
} }
public function testMarkTooManyMultipleArticles(): void { public function testMarkTooManyMultipleArticles(): void {
$this->assertSame(7, Arsse::$db->articleMark($this->user, ['read' => false,'starred' => true], (new Context)->articles(range(1, Database::LIMIT_SET_SIZE * 3)))); $setSize = (new \ReflectionClassConstant(Database::class, "LIMIT_SET_SIZE"))->getValue();
$this->assertSame(7, Arsse::$db->articleMark($this->user, ['read' => false,'starred' => true], (new Context)->articles(range(1, $setSize * 3))));
} }
public function testMarkAMissingArticle(): void { public function testMarkAMissingArticle(): void {
@ -971,10 +973,11 @@ trait SeriesArticle {
} }
public function testCountArticles(): void { public function testCountArticles(): void {
$setSize = (new \ReflectionClassConstant(Database::class, "LIMIT_SET_SIZE"))->getValue();
$this->assertSame(2, Arsse::$db->articleCount("john.doe@example.com", (new Context)->starred(true))); $this->assertSame(2, Arsse::$db->articleCount("john.doe@example.com", (new Context)->starred(true)));
$this->assertSame(4, Arsse::$db->articleCount("john.doe@example.com", (new Context)->folder(1))); $this->assertSame(4, Arsse::$db->articleCount("john.doe@example.com", (new Context)->folder(1)));
$this->assertSame(0, Arsse::$db->articleCount("jane.doe@example.com", (new Context)->starred(true))); $this->assertSame(0, Arsse::$db->articleCount("jane.doe@example.com", (new Context)->starred(true)));
$this->assertSame(10, Arsse::$db->articleCount("john.doe@example.com", (new Context)->articles(range(1, Database::LIMIT_SET_SIZE * 3)))); $this->assertSame(10, Arsse::$db->articleCount("john.doe@example.com", (new Context)->articles(range(1, $setSize * 3))));
} }
public function testCountArticlesWithoutAuthority(): void { public function testCountArticlesWithoutAuthority(): void {

View file

@ -35,10 +35,10 @@ class TestDatabase extends \JKingWeb\Arsse\Test\AbstractTest {
} }
public function provideInClauses(): iterable { public function provideInClauses(): iterable {
$l = Database::LIMIT_SET_SIZE + 1; $l = (new \ReflectionClassConstant(Database::class, "LIMIT_SET_SIZE"))->getValue() + 1;
$strings = array_fill(0, $l, ""); $strings = array_fill(0, $l, "");
$ints = range(1, $l); $ints = range(1, $l);
$longString = str_repeat("0", Database::LIMIT_SET_STRING_LENGTH + 1); $longString = str_repeat("0", (new \ReflectionClassConstant(Database::class, "LIMIT_SET_STRING_LENGTH"))->getValue() + 1);
$params = implode(",", array_fill(0, $l, "?")); $params = implode(",", array_fill(0, $l, "?"));
$intList = implode(",", $ints); $intList = implode(",", $ints);
$stringList = implode(",", array_fill(0, $l, "''")); $stringList = implode(",", array_fill(0, $l, "''"));
@ -70,9 +70,10 @@ class TestDatabase extends \JKingWeb\Arsse\Test\AbstractTest {
} }
public function provideSearchClauses(): iterable { public function provideSearchClauses(): iterable {
$terms = array_fill(0, Database::LIMIT_SET_SIZE + 1, "a"); $setSize = (new \ReflectionClassConstant(Database::class, "LIMIT_SET_SIZE"))->getValue();
$clause = array_fill(0, Database::LIMIT_SET_SIZE + 1, "test like '%a%' escape '^'"); $terms = array_fill(0, $setSize + 1, "a");
$longString = str_repeat("0", Database::LIMIT_SET_STRING_LENGTH + 1); $clause = array_fill(0, $setSize + 1, "test like '%a%' escape '^'");
$longString = str_repeat("0", (new \ReflectionClassConstant(Database::class, "LIMIT_SET_STRING_LENGTH"))->getValue() + 1);
return [ return [
["test like ? escape '^'", ["%a%"], ["a"], ["test"], true], ["test like ? escape '^'", ["%a%"], ["a"], ["test"], true],
["(col1 like ? escape '^' or col2 like ? escape '^')", ["%a%", "%a%"], ["a"], ["col1", "col2"], true], ["(col1 like ? escape '^' or col2 like ? escape '^')", ["%a%", "%a%"], ["a"], ["col1", "col2"], true],

View file

@ -37,8 +37,9 @@ class TestBasic extends \JKingWeb\Arsse\Test\AbstractTest {
* @depends testSetLanguage * @depends testSetLanguage
*/ */
public function testLoadInternalStrings(): void { public function testLoadInternalStrings(): void {
$exp = (new \ReflectionClassConstant(TestClass::class, "REQUIRED"))->getValue();
$this->assertEquals("", $this->l->set("", true)); $this->assertEquals("", $this->l->set("", true));
$this->assertCount(sizeof(TestClass::REQUIRED), $this->l->dump()); $this->assertCount(sizeof($exp), $this->l->dump());
} }
/** /**

View file

@ -411,10 +411,11 @@ class TestValueInfo extends \JKingWeb\Arsse\Test\AbstractTest {
$this->assertEquals($exp, I::normalize($value, $type | I::M_ARRAY, "iso8601"), "Failed test #$index"); $this->assertEquals($exp, I::normalize($value, $type | I::M_ARRAY, "iso8601"), "Failed test #$index");
} }
// Date-to-string format tests // Date-to-string format tests
$dateFormats = (new \ReflectionClassConstant(I::class, "DATE_FORMATS"))->getValue();
$test = new \DateTimeImmutable("now", new \DateTimezone("UTC")); $test = new \DateTimeImmutable("now", new \DateTimezone("UTC"));
$exp = $test->format(I::DATE_FORMATS['iso8601'][1]); $exp = $test->format($dateFormats['iso8601'][1]);
$this->assertSame($exp, I::normalize($test, I::T_STRING, null), "Failed test for null output date format"); $this->assertSame($exp, I::normalize($test, I::T_STRING, null), "Failed test for null output date format");
foreach (I::DATE_FORMATS as $name => $formats) { foreach ($dateFormats as $name => $formats) {
$exp = $test->format($formats[1]); $exp = $test->format($formats[1]);
$this->assertSame($exp, I::normalize($test, I::T_STRING, null, $name), "Failed test for output date format '$name'"); $this->assertSame($exp, I::normalize($test, I::T_STRING, null, $name), "Failed test for output date format '$name'");
} }

View file

@ -488,8 +488,10 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
} }
public function testListFeedIcons(): void { public function testListFeedIcons(): void {
$iconType = (new \ReflectionClassConstant(API::class, "GENERIC_ICON_TYPE"))->getValue();
$iconData = (new \ReflectionClassConstant(API::class, "GENERIC_ICON_DATA"))->getValue();
$act = $this->h->dispatch($this->req("api&favicons")); $act = $this->h->dispatch($this->req("api&favicons"));
$exp = new JsonResponse(['favicons' => [['id' => 0, 'data' => API::GENERIC_ICON_TYPE.",".API::GENERIC_ICON_DATA]]]); $exp = new JsonResponse(['favicons' => [['id' => 0, 'data' => $iconType.",".$iconData]]]);
$this->assertMessage($exp, $act); $this->assertMessage($exp, $act);
} }

View file

@ -997,6 +997,7 @@ LONG_STRING;
['id' => 3, 'name' => "Hardware"], ['id' => 3, 'name' => "Hardware"],
['id' => 1, 'name' => "Politics"], ['id' => 1, 'name' => "Politics"],
]; ];
$labelOffset = (new \ReflectionClassConstant(API::class, "LABEL_OFFSET"))->getValue();
// set of various mocks for testing // set of various mocks for testing
\Phake::when(Arsse::$db)->labelAdd(Arsse::$user->id, $db[0])->thenReturn(2)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call \Phake::when(Arsse::$db)->labelAdd(Arsse::$user->id, $db[0])->thenReturn(2)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call
\Phake::when(Arsse::$db)->labelAdd(Arsse::$user->id, $db[1])->thenReturn(3)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call \Phake::when(Arsse::$db)->labelAdd(Arsse::$user->id, $db[1])->thenReturn(3)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call
@ -1007,14 +1008,14 @@ LONG_STRING;
\Phake::when(Arsse::$db)->labelAdd(Arsse::$user->id, ['name' => ""])->thenThrow(new ExceptionInput("missing")); \Phake::when(Arsse::$db)->labelAdd(Arsse::$user->id, ['name' => ""])->thenThrow(new ExceptionInput("missing"));
\Phake::when(Arsse::$db)->labelAdd(Arsse::$user->id, ['name' => " "])->thenThrow(new ExceptionInput("whitespace")); \Phake::when(Arsse::$db)->labelAdd(Arsse::$user->id, ['name' => " "])->thenThrow(new ExceptionInput("whitespace"));
// correctly add two labels // correctly add two labels
$exp = $this->respGood((-1 * API::LABEL_OFFSET) - 2); $exp = $this->respGood((-1 * $labelOffset) - 2);
$this->assertMessage($exp, $this->req($in[0])); $this->assertMessage($exp, $this->req($in[0]));
$exp = $this->respGood((-1 * API::LABEL_OFFSET) - 3); $exp = $this->respGood((-1 * $labelOffset) - 3);
$this->assertMessage($exp, $this->req($in[1])); $this->assertMessage($exp, $this->req($in[1]));
// attempt to add the two labels again // attempt to add the two labels again
$exp = $this->respGood((-1 * API::LABEL_OFFSET) - 2); $exp = $this->respGood((-1 * $labelOffset) - 2);
$this->assertMessage($exp, $this->req($in[0])); $this->assertMessage($exp, $this->req($in[0]));
$exp = $this->respGood((-1 * API::LABEL_OFFSET) - 3); $exp = $this->respGood((-1 * $labelOffset) - 3);
$this->assertMessage($exp, $this->req($in[1])); $this->assertMessage($exp, $this->req($in[1]));
\Phake::verify(Arsse::$db)->labelPropertiesGet(Arsse::$user->id, "Software", true); \Phake::verify(Arsse::$db)->labelPropertiesGet(Arsse::$user->id, "Software", true);
\Phake::verify(Arsse::$db)->labelPropertiesGet(Arsse::$user->id, "Hardware", true); \Phake::verify(Arsse::$db)->labelPropertiesGet(Arsse::$user->id, "Hardware", true);