Browse Source

Add class constant visibility

rpm
J. King 4 years ago
parent
commit
e60f7ea03f
  1. 14
      .php_cs.dist
  2. 2
      lib/AbstractException.php
  3. 2
      lib/Arsse.php
  4. 2
      lib/CLI.php
  5. 4
      lib/Conf.php
  6. 18
      lib/Database.php
  7. 2
      lib/Db/AbstractStatement.php
  8. 10
      lib/Db/Driver.php
  9. 4
      lib/Db/MySQL/Driver.php
  10. 2
      lib/Db/MySQL/Statement.php
  11. 2
      lib/Db/PDOStatement.php
  12. 2
      lib/Db/PostgreSQL/Driver.php
  13. 2
      lib/Db/PostgreSQL/Statement.php
  14. 10
      lib/Db/SQLite3/Driver.php
  15. 8
      lib/Db/SQLite3/Statement.php
  16. 16
      lib/Db/Statement.php
  17. 4
      lib/Feed/Exception.php
  18. 4
      lib/Lang.php
  19. 44
      lib/Misc/ValueInfo.php
  20. 4
      lib/REST.php
  21. 14
      lib/REST/Fever/API.php
  22. 6
      lib/REST/NextcloudNews/V1_2.php
  23. 38
      lib/REST/TinyTinyRSS/API.php
  24. 21
      lib/REST/TinyTinyRSS/Search.php
  25. 2
      lib/Service.php
  26. 2
      lib/User.php
  27. 6
      lib/User/Driver.php
  28. 9
      tests/cases/Database/SeriesArticle.php
  29. 11
      tests/cases/Database/TestDatabase.php
  30. 3
      tests/cases/Lang/TestBasic.php
  31. 9
      tests/cases/Misc/TestValueInfo.php
  32. 4
      tests/cases/REST/Fever/TestAPI.php
  33. 9
      tests/cases/REST/TinyTinyRSS/TestAPI.php

14
.php_cs.dist

@ -25,11 +25,8 @@ $rules = [
],
'cast_spaces' => ['space' => "single"],
'concat_space' => ['spacing' => "none"],
'declare_equal_normalize' => ['space' => "none"],
'function_typehint_space' => true,
'list_syntax' => ['syntax' => "short"],
'lowercase_cast' => true,
'lowercase_static_reference' => true,
'magic_constant_casing' => true,
'magic_method_casing' => true,
'modernize_types_casting' => true,
@ -43,8 +40,6 @@ $rules = [
'no_empty_phpdoc' => true,
'no_empty_statement' => true,
'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_short_bool_cast' => true,
'no_trailing_comma_in_singleline_array' => true,
@ -58,13 +53,20 @@ $rules = [
'pow_to_exponentiation' => true,
'return_type_declaration' => ['space_before' => "none"],
'set_type_to_cast' => true,
'short_scalar_cast' => true,
'standardize_not_equals' => true,
'trailing_comma_in_multiline_array' => true,
'unary_operator_spaces' => true,
'yoda_style' => false,
// PSR standard to apply
'@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
'braces' => ['position_after_functions_and_oop_constructs' => "same"],
'function_declaration' => ['closure_function_spacing' => "none"],

2
lib/AbstractException.php

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

2
lib/Arsse.php

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

2
lib/CLI.php

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

4
lib/Conf.php

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

18
lib/Database.php

@ -39,23 +39,23 @@ use JKingWeb\Arsse\Misc\URL;
*/
class Database {
/** The version number of the latest schema the interface is aware of */
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;
public const SCHEMA_VERSION = 6;
/** 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 */
const ASSOC_ADD = 1;
public const ASSOC_ADD = 1;
/** 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 */
const DRIVER_NAMES = [
public const DRIVER_NAMES = [
'sqlite3' => \JKingWeb\Arsse\Db\SQLite3\Driver::class,
'postgresql' => \JKingWeb\Arsse\Db\PostgreSQL\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 */
public $db;

2
lib/Db/AbstractStatement.php

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

10
lib/Db/Driver.php

@ -7,11 +7,11 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\Db;
interface Driver {
const TR_PEND = 0;
const TR_COMMIT = 1;
const TR_ROLLBACK = 2;
const TR_PEND_COMMIT = -1;
const TR_PEND_ROLLBACK = -2;
public const TR_PEND = 0;
public const TR_COMMIT = 1;
public const TR_ROLLBACK = 2;
public const TR_PEND_COMMIT = -1;
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 */
public static function create(): Driver;

4
lib/Db/MySQL/Driver.php

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

2
lib/Db/MySQL/Statement.php

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

2
lib/Db/PDOStatement.php

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

2
lib/Db/PostgreSQL/Driver.php

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

2
lib/Db/PostgreSQL/Statement.php

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

10
lib/Db/SQLite3/Driver.php

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

8
lib/Db/SQLite3/Statement.php

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

16
lib/Db/Statement.php

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

4
lib/Feed/Exception.php

@ -12,8 +12,8 @@ use GuzzleHttp\Exception\TooManyRedirectsException;
use PicoFeed\PicoFeedException;
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"];
const HTTP_ERROR_MAP = [401 => "unauthorized",403 => "forbidden",404 => "invalidUrl",408 => "timeout",410 => "invalidUrl",414 => "invalidUrl",451 => "invalidUrl"];
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"];
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) {
if ($e instanceof BadResponseException) {

4
lib/Lang.php

@ -7,8 +7,8 @@ declare(strict_types=1);
namespace JKingWeb\Arsse;
class Lang {
const DEFAULT = "en"; // fallback locale
const REQUIRED = [ // collection of absolutely required strings to handle pathological errors
public const DEFAULT = "en"; // fallback locale
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.unknown' => 'An unknown error has occurred',
'Exception.JKingWeb/Arsse/Lang/Exception.defaultFileMissing' => 'Default language file "{0}" missing',

44
lib/Misc/ValueInfo.php

@ -10,33 +10,33 @@ use JKingWeb\Arsse\ExceptionType;
class ValueInfo {
// universal
const VALID = 1 << 0;
const NULL = 1 << 1;
public const VALID = 1 << 0;
public const NULL = 1 << 1;
// integers
const ZERO = 1 << 2;
const NEG = 1 << 3;
const FLOAT = 1 << 4;
public const ZERO = 1 << 2;
public const NEG = 1 << 3;
public const FLOAT = 1 << 4;
// strings
const EMPTY = 1 << 2;
const WHITE = 1 << 3;
public const EMPTY = 1 << 2;
public const WHITE = 1 << 3;
// normalization types
const T_MIXED = 0; // pass through unchanged
const T_NULL = 1; // convert to null
const T_BOOL = 2; // convert to boolean
const T_INT = 3; // convert to integer
const T_FLOAT = 4; // convert to floating point
const T_DATE = 5; // convert to DateTimeInterface instance
const T_STRING = 6; // convert to string
const T_ARRAY = 7; // convert to array
const T_INTERVAL = 8; // convert to time interval
public const T_MIXED = 0; // pass through unchanged
public const T_NULL = 1; // convert to null
public const T_BOOL = 2; // convert to boolean
public const T_INT = 3; // convert to integer
public const T_FLOAT = 4; // convert to floating point
public const T_DATE = 5; // convert to DateTimeInterface instance
public const T_STRING = 6; // convert to string
public const T_ARRAY = 7; // convert to array
public const T_INTERVAL = 8; // convert to time interval
// normalization modes
const M_LOOSE = 0;
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
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_LOOSE = 0;
public const M_NULL = 1 << 28; // pass nulls through regardless of target type
public const M_DROP = 1 << 29; // drop the value (return null) if the type doesn't match
public const M_STRICT = 1 << 30; // throw an exception if the type doesn't match
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
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
'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

4
lib/REST.php

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

14
lib/REST/Fever/API.php

@ -19,15 +19,15 @@ use Laminas\Diactoros\Response\XmlResponse;
use Laminas\Diactoros\Response\EmptyResponse;
class API extends \JKingWeb\Arsse\REST\AbstractHandler {
const LEVEL = 3;
const GENERIC_ICON_TYPE = "image/png;base64";
const GENERIC_ICON_DATA = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAAADUlEQVQYV2NgYGBgAAAABQABijPjAAAAAABJRU5ErkJggg==";
const ACCEPTED_TYPE = "application/x-www-form-urlencoded";
public const LEVEL = 3;
protected const GENERIC_ICON_TYPE = "image/png;base64";
protected const GENERIC_ICON_DATA = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAAADUlEQVQYV2NgYGBgAAAABQABijPjAAAAAABJRU5ErkJggg==";
protected const ACCEPTED_TYPE = "application/x-www-form-urlencoded";
// 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
const PARAM_GET = [
protected const PARAM_GET = [
'api' => V::T_STRING, // this parameter requires special handling
'page' => 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,
];
// POST parameters, all of which contain meaningful values
const PARAM_POST = [
protected const PARAM_POST = [
'api_key' => V::T_STRING,
'mark' => V::T_STRING,
'as' => V::T_STRING,

6
lib/REST/NextcloudNews/V1_2.php

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

38
lib/REST/TinyTinyRSS/API.php

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

21
lib/REST/TinyTinyRSS/Search.php

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

2
lib/Service.php

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

2
lib/User.php

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

6
lib/User/Driver.php

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

9
tests/cases/Database/SeriesArticle.php

@ -421,6 +421,7 @@ trait SeriesArticle {
}
public function provideContextMatches(): iterable {
$setSize = (new \ReflectionClassConstant(Database::class, "LIMIT_SET_SIZE"))->getValue();
return [
'Blank context' => [new Context, [1,2,3,4,5,6,7,8,19,20]],
'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 articles' => [(new Context)->articles([1,20,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 2' => [(new Context)->searchTerms(["one", "first"]), [1]],
'Search title or content 3' => [(new Context)->searchTerms(["one first"]), []],
@ -816,7 +817,8 @@ trait SeriesArticle {
}
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 {
@ -971,10 +973,11 @@ trait SeriesArticle {
}
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(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(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 {

11
tests/cases/Database/TestDatabase.php

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

3
tests/cases/Lang/TestBasic.php

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

9
tests/cases/Misc/TestValueInfo.php

@ -400,9 +400,9 @@ class TestValueInfo extends \JKingWeb\Arsse\Test\AbstractTest {
public function testNormalizeComplexValues(): void {
// Array-mode tests
$tests = [
[I::T_INT | I::M_DROP, [1, 2, 2.2, 3], [1,2,null,3] ],
[I::T_INT | I::M_DROP, [1, 2, 2.2, 3], [1,2,null,3] ],
[I::T_INT, [1, 2, 2.2, 3], [1,2,2,3] ],
[I::T_INT | I::M_DROP, new Result([1, 2, 2.2, 3]), [1,2,null,3] ],
[I::T_INT | I::M_DROP, new Result([1, 2, 2.2, 3]), [1,2,null,3] ],
[I::T_INT, new Result([1, 2, 2.2, 3]), [1,2,2,3] ],
[I::T_STRING | I::M_STRICT, "Bare string", ["Bare string"]],
];
@ -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");
}
// Date-to-string format tests
$dateFormats = (new \ReflectionClassConstant(I::class, "DATE_FORMATS"))->getValue();
$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");
foreach (I::DATE_FORMATS as $name => $formats) {
foreach ($dateFormats as $name => $formats) {
$exp = $test->format($formats[1]);
$this->assertSame($exp, I::normalize($test, I::T_STRING, null, $name), "Failed test for output date format '$name'");
}

4
tests/cases/REST/Fever/TestAPI.php

@ -488,8 +488,10 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
}
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"));
$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);
}

9
tests/cases/REST/TinyTinyRSS/TestAPI.php

@ -997,6 +997,7 @@ LONG_STRING;
['id' => 3, 'name' => "Hardware"],
['id' => 1, 'name' => "Politics"],
];
$labelOffset = (new \ReflectionClassConstant(API::class, "LABEL_OFFSET"))->getValue();
// 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[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("whitespace"));
// 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]));
$exp = $this->respGood((-1 * API::LABEL_OFFSET) - 3);
$exp = $this->respGood((-1 * $labelOffset) - 3);
$this->assertMessage($exp, $this->req($in[1]));
// 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]));
$exp = $this->respGood((-1 * API::LABEL_OFFSET) - 3);
$exp = $this->respGood((-1 * $labelOffset) - 3);
$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, "Hardware", true);

Loading…
Cancel
Save