1
1
Fork 0
mirror of https://code.mensbeam.com/MensBeam/Arsse.git synced 2024-12-23 09:02:41 +00:00

Merge branch 'master' into redup

This commit is contained in:
J. King 2023-01-28 15:08:54 -05:00
commit 9d391469ad
28 changed files with 337 additions and 363 deletions

View file

@ -1,3 +1,9 @@
Version 0.??.? (????-??-??)
===========================
Changes:
- Require PHP 7.3
Version 0.10.4 (2023-01-24) Version 0.10.4 (2023-01-24)
=========================== ===========================

View file

@ -11,6 +11,12 @@ usually prudent:
`composer install -o --no-dev` `composer install -o --no-dev`
Upgrading from 0.10.2 to 0.10.3
=============================
- PHP 7.3 is now required
Upgrading from 0.10.2 to 0.10.3 Upgrading from 0.10.2 to 0.10.3
============================= =============================

View file

@ -13,7 +13,7 @@ require_once BASE."vendor".DIRECTORY_SEPARATOR."autoload.php";
ignore_user_abort(true); ignore_user_abort(true);
ini_set("memory_limit", "-1"); ini_set("memory_limit", "-1");
ini_set("max_execution_time", "0"); ini_set("max_execution_time", "0");
// FIXME: This is required because various dependencies have yet to adjust to PHP 8.1 // NOTE: While running in the wild we don't want to spew deprecation warnings if users are ahead of us in PHP versions
error_reporting(\E_ALL & ~\E_DEPRECATED); error_reporting(\E_ALL & ~\E_DEPRECATED);
if (\PHP_SAPI === "cli") { if (\PHP_SAPI === "cli") {

View file

@ -26,7 +26,7 @@
"ext-dom": "*", "ext-dom": "*",
"nicolus/picofeed": "dev-bugfix", "nicolus/picofeed": "dev-bugfix",
"hosteurope/password-generator": "1.*", "hosteurope/password-generator": "1.*",
"docopt/docopt": "1.*", "docopt/docopt": "dev-master",
"jkingweb/druuid": "3.*", "jkingweb/druuid": "3.*",
"guzzlehttp/psr7": "1.*", "guzzlehttp/psr7": "1.*",
"laminas/laminas-httphandlerrunner": "1.*" "laminas/laminas-httphandlerrunner": "1.*"
@ -39,7 +39,7 @@
}, },
"config": { "config": {
"platform": { "platform": {
"php": "7.1.33" "php": "7.3.33"
}, },
"allow-plugins": { "allow-plugins": {
"bamarni/composer-bin-plugin": true "bamarni/composer-bin-plugin": true
@ -64,6 +64,10 @@
{ {
"type": "vcs", "type": "vcs",
"url": "https://github.com/rhukster/picoFeed" "url": "https://github.com/rhukster/picoFeed"
},
{
"type": "vcs",
"url": "https://github.com/docopt/docopt.php"
} }
] ]
} }

427
composer.lock generated
View file

@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "21a116823c3335992910966f31861811", "content-hash": "2e8ad0d4821fdfc0d975e98f2e2c3efe",
"packages": [ "packages": [
{ {
"name": "docopt/docopt", "name": "docopt/docopt",
"version": "1.0.4", "version": "dev-master",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/docopt/docopt.php.git", "url": "https://github.com/docopt/docopt.php.git",
"reference": "bf3683a16e09fa1665e493eb4d5a29469e132a4f" "reference": "5ad491cb9fc072e8bb0497061a09b0efcf25cbcf"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/docopt/docopt.php/zipball/bf3683a16e09fa1665e493eb4d5a29469e132a4f", "url": "https://api.github.com/repos/docopt/docopt.php/zipball/5ad491cb9fc072e8bb0497061a09b0efcf25cbcf",
"reference": "bf3683a16e09fa1665e493eb4d5a29469e132a4f", "reference": "5ad491cb9fc072e8bb0497061a09b0efcf25cbcf",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -26,13 +26,13 @@
"require-dev": { "require-dev": {
"phpunit/phpunit": "4.1.*" "phpunit/phpunit": "4.1.*"
}, },
"default-branch": true,
"type": "library", "type": "library",
"autoload": { "autoload": {
"classmap": [ "classmap": [
"src/docopt.php" "src/docopt.php"
] ]
}, },
"notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"MIT" "MIT"
], ],
@ -51,44 +51,56 @@
"docs" "docs"
], ],
"support": { "support": {
"issues": "https://github.com/docopt/docopt.php/issues", "source": "https://github.com/docopt/docopt.php/tree/master",
"source": "https://github.com/docopt/docopt.php/tree/1.0.4" "issues": "https://github.com/docopt/docopt.php/issues"
}, },
"time": "2019-12-03T02:48:46+00:00" "time": "2022-05-11T23:52:25+00:00"
}, },
{ {
"name": "guzzlehttp/guzzle", "name": "guzzlehttp/guzzle",
"version": "6.5.8", "version": "7.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/guzzle/guzzle.git", "url": "https://github.com/guzzle/guzzle.git",
"reference": "a52f0440530b54fa079ce76e8c5d196a42cad981" "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/a52f0440530b54fa079ce76e8c5d196a42cad981", "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba",
"reference": "a52f0440530b54fa079ce76e8c5d196a42cad981", "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-json": "*", "ext-json": "*",
"guzzlehttp/promises": "^1.0", "guzzlehttp/promises": "^1.5",
"guzzlehttp/psr7": "^1.9", "guzzlehttp/psr7": "^1.9 || ^2.4",
"php": ">=5.5", "php": "^7.2.5 || ^8.0",
"symfony/polyfill-intl-idn": "^1.17" "psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0"
},
"provide": {
"psr/http-client-implementation": "1.0"
}, },
"require-dev": { "require-dev": {
"bamarni/composer-bin-plugin": "^1.8.1",
"ext-curl": "*", "ext-curl": "*",
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", "php-http/client-integration-tests": "^3.0",
"psr/log": "^1.1" "phpunit/phpunit": "^8.5.29 || ^9.5.23",
"psr/log": "^1.1 || ^2.0 || ^3.0"
}, },
"suggest": { "suggest": {
"ext-curl": "Required for CURL handler support",
"ext-intl": "Required for Internationalized Domain Name (IDN) support",
"psr/log": "Required for using the Log middleware" "psr/log": "Required for using the Log middleware"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
},
"branch-alias": { "branch-alias": {
"dev-master": "6.5-dev" "dev-master": "7.5-dev"
} }
}, },
"autoload": { "autoload": {
@ -141,19 +153,20 @@
} }
], ],
"description": "Guzzle is a PHP HTTP client library", "description": "Guzzle is a PHP HTTP client library",
"homepage": "http://guzzlephp.org/",
"keywords": [ "keywords": [
"client", "client",
"curl", "curl",
"framework", "framework",
"http", "http",
"http client", "http client",
"psr-18",
"psr-7",
"rest", "rest",
"web service" "web service"
], ],
"support": { "support": {
"issues": "https://github.com/guzzle/guzzle/issues", "issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/6.5.8" "source": "https://github.com/guzzle/guzzle/tree/7.5.0"
}, },
"funding": [ "funding": [
{ {
@ -169,7 +182,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-06-20T22:16:07+00:00" "time": "2022-08-28T15:39:27+00:00"
}, },
{ {
"name": "guzzlehttp/promises", "name": "guzzlehttp/promises",
@ -539,21 +552,21 @@
}, },
{ {
"name": "laminas/laminas-httphandlerrunner", "name": "laminas/laminas-httphandlerrunner",
"version": "1.2.0", "version": "1.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laminas/laminas-httphandlerrunner.git", "url": "https://github.com/laminas/laminas-httphandlerrunner.git",
"reference": "e1a5dad040e0043135e8095ee27d1fbf6fb640e1" "reference": "5f94e55d93f756e8ad07b9049aeb3d6d84582d0e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/e1a5dad040e0043135e8095ee27d1fbf6fb640e1", "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/5f94e55d93f756e8ad07b9049aeb3d6d84582d0e",
"reference": "e1a5dad040e0043135e8095ee27d1fbf6fb640e1", "reference": "5f94e55d93f756e8ad07b9049aeb3d6d84582d0e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"laminas/laminas-zendframework-bridge": "^1.0", "laminas/laminas-zendframework-bridge": "^1.0",
"php": "^7.1", "php": "^7.3 || ~8.0.0 || ~8.1.0",
"psr/http-message": "^1.0", "psr/http-message": "^1.0",
"psr/http-message-implementation": "^1.0", "psr/http-message-implementation": "^1.0",
"psr/http-server-handler": "^1.0" "psr/http-server-handler": "^1.0"
@ -563,15 +576,13 @@
}, },
"require-dev": { "require-dev": {
"laminas/laminas-coding-standard": "~1.0.0", "laminas/laminas-coding-standard": "~1.0.0",
"laminas/laminas-diactoros": "^1.7 || ^2.1.1", "laminas/laminas-diactoros": "^2.8.0",
"phpunit/phpunit": "^7.0.2" "phpunit/phpunit": "^9.5.9",
"psalm/plugin-phpunit": "^0.16.1",
"vimeo/psalm": "^4.10.0"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": {
"dev-master": "1.2.x-dev",
"dev-develop": "1.3.x-dev"
},
"laminas": { "laminas": {
"config-provider": "Laminas\\HttpHandlerRunner\\ConfigProvider" "config-provider": "Laminas\\HttpHandlerRunner\\ConfigProvider"
} }
@ -608,40 +619,37 @@
"type": "community_bridge" "type": "community_bridge"
} }
], ],
"time": "2020-06-03T15:52:17+00:00" "time": "2021-09-22T09:17:54+00:00"
}, },
{ {
"name": "laminas/laminas-xml", "name": "laminas/laminas-xml",
"version": "1.2.0", "version": "1.4.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laminas/laminas-xml.git", "url": "https://github.com/laminas/laminas-xml.git",
"reference": "879cc66d1bba6a37705e98074f52a6960c220020" "reference": "dcadeefdb6d7ed6b39d772b47e3845003d6ea60f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-xml/zipball/879cc66d1bba6a37705e98074f52a6960c220020", "url": "https://api.github.com/repos/laminas/laminas-xml/zipball/dcadeefdb6d7ed6b39d772b47e3845003d6ea60f",
"reference": "879cc66d1bba6a37705e98074f52a6960c220020", "reference": "dcadeefdb6d7ed6b39d772b47e3845003d6ea60f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"laminas/laminas-zendframework-bridge": "^1.0", "ext-dom": "*",
"php": "^5.6 || ^7.0" "ext-simplexml": "*",
"php": "^7.3 || ~8.0.0 || ~8.1.0"
}, },
"replace": { "conflict": {
"zendframework/zendxml": "self.version" "zendframework/zendxml": "*"
}, },
"require-dev": { "require-dev": {
"ext-iconv": "*",
"laminas/laminas-coding-standard": "~1.0.0", "laminas/laminas-coding-standard": "~1.0.0",
"phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4" "phpunit/phpunit": "^9.5.8",
"squizlabs/php_codesniffer": "3.6.1 as 2.9999999.9999999"
}, },
"type": "library", "type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev",
"dev-develop": "1.3.x-dev"
}
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Laminas\\Xml\\": "src/" "Laminas\\Xml\\": "src/"
@ -660,34 +668,41 @@
], ],
"support": { "support": {
"chat": "https://laminas.dev/chat", "chat": "https://laminas.dev/chat",
"docs": "https://docs.laminas.dev/laminas-xml/",
"forum": "https://discourse.laminas.dev", "forum": "https://discourse.laminas.dev",
"issues": "https://github.com/laminas/laminas-xml/issues", "issues": "https://github.com/laminas/laminas-xml/issues",
"rss": "https://github.com/laminas/laminas-xml/releases.atom", "rss": "https://github.com/laminas/laminas-xml/releases.atom",
"source": "https://github.com/laminas/laminas-xml" "source": "https://github.com/laminas/laminas-xml"
}, },
"time": "2019-12-31T18:05:42+00:00" "funding": [
{
"url": "https://funding.communitybridge.org/projects/laminas-project",
"type": "community_bridge"
}
],
"time": "2021-11-30T02:16:35+00:00"
}, },
{ {
"name": "laminas/laminas-zendframework-bridge", "name": "laminas/laminas-zendframework-bridge",
"version": "1.1.1", "version": "1.4.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laminas/laminas-zendframework-bridge.git", "url": "https://github.com/laminas/laminas-zendframework-bridge.git",
"reference": "6ede70583e101030bcace4dcddd648f760ddf642" "reference": "88bf037259869891afce6504cacc4f8a07b24d0f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/6ede70583e101030bcace4dcddd648f760ddf642", "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/88bf037259869891afce6504cacc4f8a07b24d0f",
"reference": "6ede70583e101030bcace4dcddd648f760ddf642", "reference": "88bf037259869891afce6504cacc4f8a07b24d0f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^5.6 || ^7.0 || ^8.0" "php": "^7.3 || ~8.0.0 || ~8.1.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.1 || ^9.3", "phpunit/phpunit": "^9.3",
"squizlabs/php_codesniffer": "^3.5" "psalm/plugin-phpunit": "^0.15.1",
"squizlabs/php_codesniffer": "^3.5",
"vimeo/psalm": "^4.6"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
@ -726,7 +741,7 @@
"type": "community_bridge" "type": "community_bridge"
} }
], ],
"time": "2020-09-14T14:23:00+00:00" "time": "2021-12-21T14:34:37+00:00"
}, },
{ {
"name": "nicolus/picofeed", "name": "nicolus/picofeed",
@ -792,6 +807,58 @@
}, },
"time": "2022-09-23T17:42:18+00:00" "time": "2022-09-23T17:42:18+00:00"
}, },
{
"name": "psr/http-client",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-client.git",
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
"shasum": ""
},
"require": {
"php": "^7.0 || ^8.0",
"psr/http-message": "^1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Client\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP clients",
"homepage": "https://github.com/php-fig/http-client",
"keywords": [
"http",
"http-client",
"psr",
"psr-18"
],
"support": {
"source": "https://github.com/php-fig/http-client/tree/master"
},
"time": "2020-06-29T06:28:15+00:00"
},
{ {
"name": "psr/http-message", "name": "psr/http-message",
"version": "1.0.1", "version": "1.0.1",
@ -997,131 +1064,35 @@
"time": "2019-03-08T08:55:37+00:00" "time": "2019-03-08T08:55:37+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-idn", "name": "symfony/deprecation-contracts",
"version": "v1.27.0", "version": "v2.5.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git", "url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "639084e360537a19f9ee352433b84ce831f3d2da" "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da", "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
"reference": "639084e360537a19f9ee352433b84ce831f3d2da", "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
"shasum": ""
},
"require": {
"php": ">=7.1",
"symfony/polyfill-intl-normalizer": "^1.10",
"symfony/polyfill-php72": "^1.10"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Intl\\Idn\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Laurent Bassin",
"email": "laurent@bassin.info"
},
{
"name": "Trevor Rowbotham",
"email": "trevor.rowbotham@pm.me"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"idn",
"intl",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.1" "php": ">=7.1"
}, },
"suggest": {
"ext-intl": "For best performance"
},
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.27-dev" "dev-main": "2.5-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/contracts",
"url": "https://github.com/symfony/polyfill" "url": "https://github.com/symfony/contracts"
} }
}, },
"autoload": { "autoload": {
"files": [ "files": [
"bootstrap.php" "function.php"
],
"psr-4": {
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
},
"classmap": [
"Resources/stubs"
] ]
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
@ -1138,18 +1109,10 @@
"homepage": "https://symfony.com/contributors" "homepage": "https://symfony.com/contributors"
} }
], ],
"description": "Symfony polyfill for intl's Normalizer class and related functions", "description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"keywords": [
"compatibility",
"intl",
"normalizer",
"polyfill",
"portable",
"shim"
],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2"
}, },
"funding": [ "funding": [
{ {
@ -1165,111 +1128,42 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-11-03T14:55:06+00:00" "time": "2022-01-02T09:53:40+00:00"
},
{
"name": "symfony/polyfill-php72",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "869329b1e9894268a8a61dabb69153029b7a8c97"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97",
"reference": "869329b1e9894268a8a61dabb69153029b7a8c97",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php72\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
} }
], ],
"packages-dev": [ "packages-dev": [
{ {
"name": "bamarni/composer-bin-plugin", "name": "bamarni/composer-bin-plugin",
"version": "v1.5.0", "version": "1.8.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/bamarni/composer-bin-plugin.git", "url": "https://github.com/bamarni/composer-bin-plugin.git",
"reference": "49934ffea764864788334c1485fbb08a4b852031" "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/49934ffea764864788334c1485fbb08a4b852031", "url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880",
"reference": "49934ffea764864788334c1485fbb08a4b852031", "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"composer-plugin-api": "^1.0 || ^2.0", "composer-plugin-api": "^2.0",
"php": "^5.5.9 || ^7.0 || ^8.0" "php": "^7.2.5 || ^8.0"
}, },
"require-dev": { "require-dev": {
"composer/composer": "^1.0 || ^2.0", "composer/composer": "^2.0",
"symfony/console": "^2.5 || ^3.0 || ^4.0" "ext-json": "*",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-phpunit": "^1.1",
"phpunit/phpunit": "^8.5 || ^9.5",
"symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
"symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
"symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0"
}, },
"type": "composer-plugin", "type": "composer-plugin",
"extra": { "extra": {
"class": "Bamarni\\Composer\\Bin\\Plugin" "class": "Bamarni\\Composer\\Bin\\BamarniBinPlugin"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@ -1291,15 +1185,16 @@
], ],
"support": { "support": {
"issues": "https://github.com/bamarni/composer-bin-plugin/issues", "issues": "https://github.com/bamarni/composer-bin-plugin/issues",
"source": "https://github.com/bamarni/composer-bin-plugin/tree/v1.5.0" "source": "https://github.com/bamarni/composer-bin-plugin/tree/1.8.2"
}, },
"time": "2022-02-22T21:01:25+00:00" "time": "2022-10-31T08:38:03+00:00"
} }
], ],
"aliases": [], "aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": { "stability-flags": {
"nicolus/picofeed": 20 "nicolus/picofeed": 20,
"docopt/docopt": 20
}, },
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
@ -1313,7 +1208,7 @@
}, },
"platform-dev": [], "platform-dev": [],
"platform-overrides": { "platform-overrides": {
"php": "7.1.33" "php": "7.3.33"
}, },
"plugin-api-version": "2.3.0" "plugin-api-version": "2.3.0"
} }

8
dist/arch/PKGBUILD vendored
View file

@ -1,6 +1,6 @@
# Maintainer: J. King <jking@jkingweb.ca> # Maintainer: J. King <jking@jkingweb.ca>
pkgname="arsse" pkgname="arsse"
pkgver=0.9.2 pkgver=0.10.4
pkgrel=1 pkgrel=1
epoch= epoch=
pkgdesc="Multi-protocol RSS/Atom newsfeed synchronization server" pkgdesc="Multi-protocol RSS/Atom newsfeed synchronization server"
@ -14,7 +14,7 @@ optdepends=("nginx: HTTP server"
"apache>=2.4: HTTP server" "apache>=2.4: HTTP server"
"percona-server: Alternate database" "percona-server: Alternate database"
"postgresql>=10: Alternate database" "postgresql>=10: Alternate database"
"php-pgsql>=7.1: PostgreSQL database support") "php-pgsql>=7.3: PostgreSQL database support")
backup=("etc/webapps/arsse/config.php" backup=("etc/webapps/arsse/config.php"
"etc/php/php-fpm.d/arsse.conf" "etc/php/php-fpm.d/arsse.conf"
"etc/webapps/arsse/nginx/example.conf" "etc/webapps/arsse/nginx/example.conf"
@ -24,12 +24,12 @@ backup=("etc/webapps/arsse/config.php"
"etc/webapps/arsse/apache/example.conf" "etc/webapps/arsse/apache/example.conf"
"etc/webapps/arsse/apache/arsse.conf" "etc/webapps/arsse/apache/arsse.conf"
"etc/webapps/arsse/apache/arsse-loc.conf") "etc/webapps/arsse/apache/arsse-loc.conf")
source=("arsse-0.9.2.tar.gz") source=("arsse-0.10.4.tar.gz")
md5sums=("SKIP") md5sums=("SKIP")
package() { package() {
# define runtime dependencies # define runtime dependencies
depends=("php>=7.1" "php-intl>=7.1" "php-sqlite>=7.1" "php-fpm>=7.1") depends=("php>=7.3" "php-intl>=7.3" "php-sqlite>=7.3" "php-fpm>=7.3")
# create most directories necessary for the final package # create most directories necessary for the final package
cd "$pkgdir" cd "$pkgdir"
mkdir -p "usr/share/webapps/arsse" "usr/share/doc/arsse" "usr/share/licenses/arsse" "usr/lib/systemd/system" "usr/lib/sysusers.d" "usr/lib/tmpfiles.d" "etc/php/php-fpm.d" "etc/webapps/arsse" mkdir -p "usr/share/webapps/arsse" "usr/share/doc/arsse" "usr/share/licenses/arsse" "usr/lib/systemd/system" "usr/lib/sysusers.d" "usr/lib/tmpfiles.d" "etc/php/php-fpm.d" "etc/webapps/arsse"

View file

@ -1,6 +1,6 @@
# Maintainer: J. King <jking@jkingweb.ca> # Maintainer: J. King <jking@jkingweb.ca>
pkgname="arsse-git" pkgname="arsse-git"
pkgver=0.9.2 pkgver=0.10.4
pkgrel=1 pkgrel=1
epoch= epoch=
pkgdesc="Multi-protocol RSS/Atom newsfeed synchronization server, bugfix-testing version" pkgdesc="Multi-protocol RSS/Atom newsfeed synchronization server, bugfix-testing version"
@ -9,14 +9,14 @@ url="https://thearsse.com/"
license=("MIT") license=("MIT")
provides=("arsse") provides=("arsse")
conflicts=("arsse") conflicts=("arsse")
depends=("php>=7.1" "php-intl>=7.1" "php-sqlite>=7.1") depends=("php>=7.3" "php-intl>=7.3" "php-sqlite>=7.3")
makedepends=("composer" "pandoc") makedepends=("composer" "pandoc")
checkdepends=() checkdepends=()
optdepends=("nginx: HTTP server" optdepends=("nginx: HTTP server"
"apache>=2.4: HTTP server" "apache>=2.4: HTTP server"
"percona-server: Alternate database" "percona-server: Alternate database"
"postgresql>=10: Alternate database" "postgresql>=10: Alternate database"
"php-pgsql>=7.1: PostgreSQL database support") "php-pgsql>=7.3: PostgreSQL database support")
backup=("etc/webapps/arsse/config.php" backup=("etc/webapps/arsse/config.php"
"etc/php/php-fpm.d/arsse.conf" "etc/php/php-fpm.d/arsse.conf"
"etc/webapps/arsse/nginx/example.conf" "etc/webapps/arsse/nginx/example.conf"
@ -46,7 +46,7 @@ build() {
package() { package() {
# define runtime dependencies # define runtime dependencies
depends=("php>=7.1" "php-intl>=7.1" "php-sqlite>=7.1" "php-fpm>=7.1") depends=("php>=7.3" "php-intl>=7.3" "php-sqlite>=7.3" "php-fpm>=7.3")
# create most directories necessary for the final package # create most directories necessary for the final package
cd "$pkgdir" cd "$pkgdir"
mkdir -p "usr/share/webapps/arsse" "usr/share/doc/arsse" "usr/share/licenses/arsse" "usr/lib/systemd/system" "usr/lib/sysusers.d" "usr/lib/tmpfiles.d" "etc/php/php-fpm.d" "etc/webapps/arsse" mkdir -p "usr/share/webapps/arsse" "usr/share/doc/arsse" "usr/share/licenses/arsse" "usr/lib/systemd/system" "usr/lib/sysusers.d" "usr/lib/tmpfiles.d" "etc/php/php-fpm.d" "etc/webapps/arsse"

2
dist/debian/control vendored
View file

@ -20,7 +20,7 @@ Description: Multi-protocol RSS/Atom newsfeed synchronization server
server. server.
Depends: ${misc:Depends}, Depends: ${misc:Depends},
dbconfig-sqlite3 | dbconfig-pgsql | dbconfig-mysql | dbconfig-no-thanks, dbconfig-sqlite3 | dbconfig-pgsql | dbconfig-mysql | dbconfig-no-thanks,
php (>= 7.1.0), php (>= 7.3.0),
php-cli, php-cli,
php-intl, php-intl,
php-json, php-json,

View file

@ -11,7 +11,7 @@ The Arsse has the following requirements:
- A Web server such as: - A Web server such as:
- [Nginx](https://nginx.org) - [Nginx](https://nginx.org)
- [Apache HTTP server](https://httpd.apache.org) 2.4 or later - [Apache HTTP server](https://httpd.apache.org) 2.4 or later
- PHP 7.1.0 or later with the following extensions: - PHP 7.3.0 or later with the following extensions:
- [intl](https://php.net/manual/en/book.intl.php), [json](https://php.net/manual/en/book.json.php), [hash](https://php.net/manual/en/book.hash.php), [filter](https://php.net/manual/en/book.filter.php), and [dom](https://php.net/manual/en/book.dom.php) - [intl](https://php.net/manual/en/book.intl.php), [json](https://php.net/manual/en/book.json.php), [hash](https://php.net/manual/en/book.hash.php), [filter](https://php.net/manual/en/book.filter.php), and [dom](https://php.net/manual/en/book.dom.php)
- [simplexml](https://php.net/manual/en/book.simplexml.php), and [iconv](https://php.net/manual/en/book.iconv.php) - [simplexml](https://php.net/manual/en/book.simplexml.php), and [iconv](https://php.net/manual/en/book.iconv.php)
- One of: - One of:

View file

@ -7,13 +7,15 @@
declare(strict_types=1); declare(strict_types=1);
namespace JKingWeb\Arsse; namespace JKingWeb\Arsse;
use AllowDynamicProperties;
use JKingWeb\Arsse\Misc\ValueInfo as Value; use JKingWeb\Arsse\Misc\ValueInfo as Value;
/** 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.
* All public properties are configuration parameters that may be set by the server administrator. */ * All public properties are configuration parameters that may be set by the server administrator. */
class Conf { #[AllowDynamicProperties]
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";

View file

@ -1362,12 +1362,12 @@ class Database {
"UPDATE arsse_subscriptions SET feed_title = ?, source = ?, updated = CURRENT_TIMESTAMP, last_mod = ?, etag = ?, err_count = 0, err_msg = '', next_fetch = ?, size = ?, icon = ? WHERE id = ?", "UPDATE arsse_subscriptions SET feed_title = ?, source = ?, updated = CURRENT_TIMESTAMP, last_mod = ?, etag = ?, err_count = 0, err_msg = '', next_fetch = ?, size = ?, icon = ? WHERE id = ?",
["str", "str", "datetime", "strict str", "datetime", "int", "int", "int"] ["str", "str", "datetime", "strict str", "datetime", "int", "int", "int"]
)->run( )->run(
$feed->data->title, $feed->title,
$feed->data->siteUrl, $feed->siteUrl,
$feed->lastModified, $feed->lastModified,
$feed->resource->getEtag(), $feed->etag,
$feed->nextFetch, $feed->nextFetch,
sizeof($feed->data->items), sizeof($feed->items),
$icon, $icon,
$subID $subID
); );

View file

@ -20,6 +20,7 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
public const SQLITE_MISMATCH = 20; public const SQLITE_MISMATCH = 20;
protected $db; protected $db;
protected $collator;
public function __construct() { public function __construct() {
// check to make sure required extension is loaded // check to make sure required extension is loaded

View file

@ -6,6 +6,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace JKingWeb\Arsse; namespace JKingWeb\Arsse;
use JKingWeb\Arsse\Feed\Item;
use JKingWeb\Arsse\Misc\Date; use JKingWeb\Arsse\Misc\Date;
use JKingWeb\Arsse\Rule\Rule; use JKingWeb\Arsse\Rule\Rule;
use PicoFeed\PicoFeedException; use PicoFeed\PicoFeedException;
@ -16,62 +17,62 @@ use PicoFeed\Reader\Favicon;
use PicoFeed\Scraper\Scraper; use PicoFeed\Scraper\Scraper;
class Feed { class Feed {
public $data = null; public $title;
public $siteUrl;
public $iconUrl; public $iconUrl;
public $iconType; public $iconType;
public $iconData; public $iconData;
public $resource;
public $modified = false; public $modified = false;
public $lastModified; public $lastModified;
public $etag;
public $nextFetch; public $nextFetch;
public $items = [];
public $newItems = []; public $newItems = [];
public $changedItems = []; public $changedItems = [];
public $filteredItems = []; public $filteredItems = [];
public static function discover(string $url, string $username = '', string $password = ''): string { public static function discover(string $url, string $username = '', string $password = ''): string {
// fetch the candidate feed // fetch the candidate feed
$f = self::download($url, "", "", $username, $password); [$client, $reader] = self::download($url, "", "", $username, $password);
if ($f->reader->detectFormat($f->getContent())) { if ($reader->detectFormat($client->getContent())) {
// if the prospective URL is a feed, use it // if the prospective URL is a feed, use it
$out = $url; $out = $url;
} else { } else {
$links = $f->reader->find($f->getUrl(), $f->getContent()); $links = $reader->find($client->getUrl(), $client->getContent());
if (!$links) { if (!$links) {
// work around a PicoFeed memory leak
libxml_use_internal_errors(false);
throw new Feed\Exception("", ['url' => $url], new \PicoFeed\Reader\SubscriptionNotFoundException('Unable to find a subscription')); throw new Feed\Exception("", ['url' => $url], new \PicoFeed\Reader\SubscriptionNotFoundException('Unable to find a subscription'));
} else { } else {
$out = $links[0]; $out = $links[0];
} }
} }
// work around a PicoFeed memory leak
libxml_use_internal_errors(false);
return $out; return $out;
} }
public static function discoverAll(string $url, string $username = '', string $password = ''): array { public static function discoverAll(string $url, string $username = '', string $password = ''): array {
// fetch the candidate feed // fetch the candidate feed
$f = self::download($url, "", "", $username, $password); [$client, $reader] = self::download($url, "", "", $username, $password);
if ($f->reader->detectFormat($f->getContent())) { if ($reader->detectFormat($client->getContent())) {
// if the prospective URL is a feed, use it // if the prospective URL is a feed, use it
return [$url]; return [$url];
} else { } else {
return $f->reader->find($f->getUrl(), $f->getContent()); return $reader->find($client->getUrl(), $client->getContent());
} }
} }
public function __construct(int $feedID = null, string $url, string $lastModified = '', string $etag = '', string $username = '', string $password = '', bool $scrape = false) { public function __construct(int $feedID = null, string $url, string $lastModified = '', string $etag = '', string $username = '', string $password = '', bool $scrape = false) {
// fetch the feed // fetch the feed
$this->resource = self::download($url, $lastModified, $etag, $username, $password); [$client, $reader] = self::download($url, $lastModified, $etag, $username, $password);
// format the HTTP Last-Modified date returned // format the HTTP Last-Modified date returned
$lastMod = $this->resource->getLastModified(); $lastMod = $client->getLastModified();
if (strlen($lastMod ?? "")) { if (strlen($lastMod ?? "")) {
$this->lastModified = Date::normalize($lastMod, "http"); $this->lastModified = Date::normalize($lastMod, "http");
} }
$this->modified = $this->resource->isModified(); $this->modified = $client->isModified();
//parse the feed, if it has been modified // get the ETag
$this->etag = $client->getEtag();
// parse the feed, if it has been modified
if ($this->modified) { if ($this->modified) {
$this->parse(); $this->parse($client, $reader);
// ascertain whether there are any articles not in the database // ascertain whether there are any articles not in the database
$this->matchToDatabase($feedID); $this->matchToDatabase($feedID);
// if caching header fields are not sent by the server, try to ascertain a last-modified date from the feed contents // if caching header fields are not sent by the server, try to ascertain a last-modified date from the feed contents
@ -112,12 +113,11 @@ class Feed {
return $config; return $config;
} }
protected static function download(string $url, string $lastModified, string $etag, string $username, string $password): Client { protected static function download(string $url, string $lastModified, string $etag, string $username, string $password): array {
try { try {
$reader = new Reader(self::configure()); $reader = new Reader(self::configure());
$client = $reader->download($url, $lastModified, $etag, $username, $password); $client = $reader->download($url, $lastModified, $etag, $username, $password);
$client->reader = $reader; return [$client, $reader];
return $client;
} catch (PicoFeedException $e) { } catch (PicoFeedException $e) {
throw new Feed\Exception("", ['url' => $url], $e); // @codeCoverageIgnore throw new Feed\Exception("", ['url' => $url], $e); // @codeCoverageIgnore
} catch (\GuzzleHttp\Exception\GuzzleException $e) { } catch (\GuzzleHttp\Exception\GuzzleException $e) {
@ -125,17 +125,17 @@ class Feed {
} }
} }
protected function parse(): void { protected function parse(Client $client, Reader $reader): void {
try { try {
$feed = $this->resource->reader->getParser( $feed = $reader->getParser(
$this->resource->getUrl(), $client->getUrl(),
$this->resource->getContent(), $client->getContent(),
$this->resource->getEncoding() $client->getEncoding()
)->execute(); )->execute();
} catch (PicoFeedException $e) { } catch (PicoFeedException $e) {
throw new Feed\Exception("", ['url' => $this->resource->getUrl()], $e); throw new Feed\Exception("", ['url' => $client->getUrl()], $e);
} catch (\GuzzleHttp\Exception\GuzzleException $e) { // @codeCoverageIgnore } catch (\GuzzleHttp\Exception\GuzzleException $e) { // @codeCoverageIgnore
throw new Feed\Exception("", ['url' => $this->resource->getUrl()], $e); // @codeCoverageIgnore throw new Feed\Exception("", ['url' => $client->getUrl()], $e); // @codeCoverageIgnore
} }
// Grab the favicon for the feed, or null if no valid icon is found // Grab the favicon for the feed, or null if no valid icon is found
@ -150,6 +150,10 @@ class Feed {
$this->iconUrl = $this->iconData = null; $this->iconUrl = $this->iconData = null;
} }
// Next gather all other feed-level information we want out of the feed
$this->siteUrl = $feed->siteUrl;
$this->title = $feed->title;
// PicoFeed does not provide valid ids when there is no id element. Its solution // PicoFeed does not provide valid ids when there is no id element. Its solution
// of hashing the url, title, and content together for the id if there is no id // of hashing the url, title, and content together for the id if there is no id
// element is stupid. Many feeds are frankenstein mixtures of Atom and RSS, but // element is stupid. Many feeds are frankenstein mixtures of Atom and RSS, but
@ -158,29 +162,38 @@ class Feed {
// only be reserved for severely broken feeds. // only be reserved for severely broken feeds.
foreach ($feed->items as $f) { foreach ($feed->items as $f) {
// Hashes used for comparison to check for updates and also to identify when an // copy the basic information of an article
$i = new Item;
$i->url = $f->url;
$i->title = $f->title;
$i->content = $f->content;
$i->author = $f->author;
$i->publishedDate = $f->publishedDate;
$i->updatedDate = $f->updatedDate;
$i->enclosureType = $f->enclosureType;
$i->enclosureUrl = $f->enclosureUrl;
// add hashes used for comparison to check for updates and also to identify when an
// id doesn't exist. // id doesn't exist.
$content = $f->content.$f->enclosureUrl.$f->enclosureType; $content = $f->content.$f->enclosureUrl.$f->enclosureType;
// if the item link URL and item title are both equal to the feed link URL, then the item has neither a link URL nor a title // if the item link URL and item title are both equal to the feed link URL, then the item has neither a link URL nor a title
if ($f->url === $feed->siteUrl && $f->title === $feed->siteUrl) { if ($f->url === $feed->siteUrl && $f->title === $feed->siteUrl) {
$f->urlTitleHash = ""; $i->urlTitleHash = "";
} else { } else {
$f->urlTitleHash = hash('sha256', $f->url.$f->title); $i->urlTitleHash = hash('sha256', $f->url.$f->title);
} }
// if the item link URL is equal to the feed link URL, it has no link URL; if there is additionally no content, these should not be hashed // if the item link URL is equal to the feed link URL, it has no link URL; if there is additionally no content, these should not be hashed
if (!strlen($content) && $f->url === $feed->siteUrl) { if (!strlen($content) && $f->url === $feed->siteUrl) {
$f->urlContentHash = ""; $i->urlContentHash = "";
} else { } else {
$f->urlContentHash = hash('sha256', $f->url.$content); $i->urlContentHash = hash('sha256', $f->url.$content);
} }
// if the item's title is the same as its link URL, it has no title; if there is additionally no content, these should not be hashed // if the item's title is the same as its link URL, it has no title; if there is additionally no content, these should not be hashed
if (!strlen($content) && $f->title === $f->url) { if (!strlen($content) && $f->title === $f->url) {
$f->titleContentHash = ""; $i->titleContentHash = "";
} else { } else {
$f->titleContentHash = hash('sha256', $f->title.$content); $i->titleContentHash = hash('sha256', $f->title.$content);
} }
$f->id = null; // next add an id; prefer an Atom ID as the item's ID
// prefer an Atom ID as the item's ID
$id = (string) $f->xml->children('http://www.w3.org/2005/Atom')->id; $id = (string) $f->xml->children('http://www.w3.org/2005/Atom')->id;
// otherwise use the RSS2 guid element // otherwise use the RSS2 guid element
if (!strlen($id)) { if (!strlen($id)) {
@ -192,11 +205,10 @@ class Feed {
} }
// otherwise there is no ID; if there is one, hash it // otherwise there is no ID; if there is one, hash it
if (strlen($id)) { if (strlen($id)) {
$f->id = hash('sha256', $id); $i->id = hash('sha256', $id);
} }
// PicoFeed also doesn't gather up categories, so we do this as well // PicoFeed also doesn't gather up categories, so we do this as well
$f->categories = [];
// first add Atom categories // first add Atom categories
foreach ($f->xml->children('http://www.w3.org/2005/Atom')->category as $c) { foreach ($f->xml->children('http://www.w3.org/2005/Atom')->category as $c) {
// if the category has a label, use that // if the category has a label, use that
@ -207,27 +219,28 @@ class Feed {
} }
// ... assuming it has that much // ... assuming it has that much
if (strlen($name)) { if (strlen($name)) {
$f->categories[] = $name; $i->categories[] = $name;
} }
} }
// next add RSS2 categories // next add RSS2 categories
foreach ($f->xml->children()->category as $c) { foreach ($f->xml->children()->category as $c) {
$name = (string) $c; $name = (string) $c;
if (strlen($name)) { if (strlen($name)) {
$f->categories[] = $name; $i->categories[] = $name;
} }
} }
// and finally try Dublin Core subjects // and finally try Dublin Core subjects
foreach ($f->xml->children('http://purl.org/dc/elements/1.1/')->subject as $c) { foreach ($f->xml->children('http://purl.org/dc/elements/1.1/')->subject as $c) {
$name = (string) $c; $name = (string) $c;
if (strlen($name)) { if (strlen($name)) {
$f->categories[] = $name; $i->categories[] = $name;
} }
} }
//sort the results //sort the results
sort($f->categories); sort($i->categories);
// add the item to the feed's list of items
$this->items[] = $i;
} }
$this->data = $feed;
} }
protected function deduplicateItems(array $items): array { protected function deduplicateItems(array $items): array {
@ -251,7 +264,7 @@ class Feed {
($item->urlContentHash && $item->urlContentHash === $check->urlContentHash) || ($item->urlContentHash && $item->urlContentHash === $check->urlContentHash) ||
($item->titleContentHash && $item->titleContentHash === $check->titleContentHash) ($item->titleContentHash && $item->titleContentHash === $check->titleContentHash)
) { ) {
if (// because newsfeeds are usually order newest-first, the later item should only be used if... if (// because newsfeeds are usually ordered newest-first, the later item should only be used if...
// the later item has an update date and the existing item does not // the later item has an update date and the existing item does not
($item->updatedDate && !$check->updatedDate) || ($item->updatedDate && !$check->updatedDate) ||
// the later item has an update date newer than the existing item's // the later item has an update date newer than the existing item's
@ -276,7 +289,7 @@ class Feed {
protected function matchToDatabase(int $feedID = null): void { protected function matchToDatabase(int $feedID = null): void {
// first perform deduplication on items // first perform deduplication on items
$items = $this->deduplicateItems($this->data->items); $items = $this->deduplicateItems($this->items);
// if we haven't been given a database feed ID to check against, all items are new // if we haven't been given a database feed ID to check against, all items are new
if (is_null($feedID)) { if (is_null($feedID)) {
$this->newItems = $items; $this->newItems = $items;
@ -429,7 +442,7 @@ class Feed {
protected function gatherDates(): array { protected function gatherDates(): array {
$dates = []; $dates = [];
foreach ($this->data->items as $item) { foreach ($this->items as $item) {
if ($item->updatedDate) { if ($item->updatedDate) {
$dates[] = $item->updatedDate->getTimestamp(); $dates[] = $item->updatedDate->getTimestamp();
} }

24
lib/Feed/Item.php Normal file
View file

@ -0,0 +1,24 @@
<?php
/** @license MIT
* Copyright 2017 J. King, Dustin Wilson et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace JKingWeb\Arsse\Feed;
class Item {
public $id;
public $url;
public $title;
public $author;
public $publishedDate;
public $updatedDate;
public $urlContentHash;
public $urlTitleHash;
public $titleContentHash;
public $content;
public $scrapedContent;
public $enclosureUrl;
public $enclosureType;
public $categories = [];
}

View file

@ -12,8 +12,7 @@ const DOCROOT = BASE."tests".DIRECTORY_SEPARATOR."docroot".DIRECTORY_SEPARATOR;
ini_set("memory_limit", "-1"); ini_set("memory_limit", "-1");
ini_set("zend.assertions", "1"); ini_set("zend.assertions", "1");
ini_set("assert.exception", "true"); ini_set("assert.exception", "true");
// FIXME: This is required because various dependencies have yet to adjust to PHP 8.1 error_reporting(\E_ALL);
error_reporting(\E_ALL & ~\E_DEPRECATED);
require_once BASE."vendor".DIRECTORY_SEPARATOR."autoload.php"; require_once BASE."vendor".DIRECTORY_SEPARATOR."autoload.php";
if (function_exists("xdebug_set_filter")) { if (function_exists("xdebug_set_filter")) {

View file

@ -21,6 +21,8 @@ use JKingWeb\Arsse\Service\Daemon;
/** @covers \JKingWeb\Arsse\CLI */ /** @covers \JKingWeb\Arsse\CLI */
class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest { class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
protected $cli;
public function setUp(): void { public function setUp(): void {
parent::setUp(); parent::setUp();
$this->cli = $this->partialMock(CLI::class); $this->cli = $this->partialMock(CLI::class);

View file

@ -29,6 +29,10 @@ abstract class AbstractTest extends \JKingWeb\Arsse\Test\AbstractTest {
protected static $drv; protected static $drv;
protected static $failureReason = ""; protected static $failureReason = "";
protected $primed = false; protected $primed = false;
protected $data;
protected $user;
protected $checkTables;
protected $series;
abstract protected function nextID(string $table): int; abstract protected function nextID(string $table): int;

View file

@ -15,6 +15,8 @@ use JKingWeb\Arsse\Misc\Date;
use JKingWeb\Arsse\Misc\ValueInfo; use JKingWeb\Arsse\Misc\ValueInfo;
trait SeriesArticle { trait SeriesArticle {
protected $fields;
protected function setUpSeriesArticle(): void { protected function setUpSeriesArticle(): void {
$this->data = [ $this->data = [
'arsse_users' => [ 'arsse_users' => [

View file

@ -10,6 +10,8 @@ use JKingWeb\Arsse\Arsse;
use JKingWeb\Arsse\Test\Result; use JKingWeb\Arsse\Test\Result;
trait SeriesFeed { trait SeriesFeed {
protected $matches;
protected function setUpSeriesFeed(): void { protected function setUpSeriesFeed(): void {
// set up the test data // set up the test data
$past = gmdate("Y-m-d H:i:s", strtotime("now - 1 minute")); $past = gmdate("Y-m-d H:i:s", strtotime("now - 1 minute"));

View file

@ -11,6 +11,8 @@ use JKingWeb\Arsse\Database;
use JKingWeb\Arsse\Context\Context; use JKingWeb\Arsse\Context\Context;
trait SeriesLabel { trait SeriesLabel {
protected $checkLabels;
protected function setUpSeriesLabel(): void { protected function setUpSeriesLabel(): void {
$this->data = [ $this->data = [
'arsse_users' => [ 'arsse_users' => [

View file

@ -10,6 +10,9 @@ use JKingWeb\Arsse\Arsse;
use JKingWeb\Arsse\Database; use JKingWeb\Arsse\Database;
trait SeriesTag { trait SeriesTag {
protected $checkMembers;
protected $checkTags;
protected function setUpSeriesTag(): void { protected function setUpSeriesTag(): void {
$this->data = [ $this->data = [
'arsse_users' => [ 'arsse_users' => [

View file

@ -17,6 +17,8 @@ class TestCreation extends \JKingWeb\Arsse\Test\AbstractTest {
protected $data; protected $data;
protected $drv; protected $drv;
protected $ch; protected $ch;
protected $files;
protected $path;
public function setUp(): void { public function setUp(): void {
if (!Driver::requirementsMet()) { if (!Driver::requirementsMet()) {

View file

@ -19,6 +19,8 @@ class TestCreation extends \JKingWeb\Arsse\Test\AbstractTest {
protected $data; protected $data;
protected $drv; protected $drv;
protected $ch; protected $ch;
protected $files;
protected $path;
public function setUp(): void { public function setUp(): void {
if (!Driver::requirementsMet()) { if (!Driver::requirementsMet()) {

View file

@ -113,26 +113,26 @@ class TestFeed extends \JKingWeb\Arsse\Test\AbstractTest {
$h0 = "0a4f0e3768c8a5e9d8d9a16545ae4ff5b097f6dac3ad49555a94a7cace68ba73"; // hash of Atom ID $h0 = "0a4f0e3768c8a5e9d8d9a16545ae4ff5b097f6dac3ad49555a94a7cace68ba73"; // hash of Atom ID
$h1 = "a135beced0236b723d12f845ff20ec22d4fc3afe1130012618f027170d57cb4e"; // hash of RSS2 GUID $h1 = "a135beced0236b723d12f845ff20ec22d4fc3afe1130012618f027170d57cb4e"; // hash of RSS2 GUID
$h2 = "205e986f4f8b3acfa281227beadb14f5e8c32c8dae4737f888c94c0df49c56f8"; // hash of Dublin Core identifier $h2 = "205e986f4f8b3acfa281227beadb14f5e8c32c8dae4737f888c94c0df49c56f8"; // hash of Dublin Core identifier
$this->assertSame($h0, $f->data->items[0]->id); $this->assertSame($h0, $f->items[0]->id);
$this->assertSame($h1, $f->data->items[1]->id); $this->assertSame($h1, $f->items[1]->id);
$this->assertSame($h2, $f->data->items[2]->id); $this->assertSame($h2, $f->items[2]->id);
// check null hashes // check null hashes
$h3 = "6287ba30f534e404e68356237e809683e311285d8b9f47d046ac58784eece052"; // URL hash $h3 = "6287ba30f534e404e68356237e809683e311285d8b9f47d046ac58784eece052"; // URL hash
$h4 = "6cbb5d2dcb11610a99eb3f633dc246690c0acf33327bf7534f95542caa8f27c4"; // title hash $h4 = "6cbb5d2dcb11610a99eb3f633dc246690c0acf33327bf7534f95542caa8f27c4"; // title hash
$h5 = "2b7c57ffa9adde92ccd1884fa1153a5bcd3211e48d99e27be5414cb078e6891c"; // content/enclosure hash $h5 = "2b7c57ffa9adde92ccd1884fa1153a5bcd3211e48d99e27be5414cb078e6891c"; // content/enclosure hash
$this->assertNotEquals("", $f->data->items[3]->urlTitleHash); $this->assertNotEquals("", $f->items[3]->urlTitleHash);
$this->assertSame($h3, $f->data->items[3]->urlContentHash); $this->assertSame($h3, $f->items[3]->urlContentHash);
$this->assertSame("", $f->data->items[3]->titleContentHash); $this->assertSame("", $f->items[3]->titleContentHash);
$this->assertNotEquals("", $f->data->items[4]->urlTitleHash); $this->assertNotEquals("", $f->items[4]->urlTitleHash);
$this->assertSame("", $f->data->items[4]->urlContentHash); $this->assertSame("", $f->items[4]->urlContentHash);
$this->assertSame($h4, $f->data->items[4]->titleContentHash); $this->assertSame($h4, $f->items[4]->titleContentHash);
$this->assertSame("", $f->data->items[5]->urlTitleHash); $this->assertSame("", $f->items[5]->urlTitleHash);
$this->assertNotEquals("", $f->data->items[5]->urlContentHash); $this->assertNotEquals("", $f->items[5]->urlContentHash);
$this->assertNotEquals("", $f->data->items[5]->titleContentHash); $this->assertNotEquals("", $f->items[5]->titleContentHash);
// check null IDs // check null IDs
$this->assertSame(null, $f->data->items[3]->id); $this->assertSame(null, $f->items[3]->id);
$this->assertSame(null, $f->data->items[4]->id); $this->assertSame(null, $f->items[4]->id);
$this->assertSame(null, $f->data->items[5]->id); $this->assertSame(null, $f->items[5]->id);
// check categories // check categories
$categories = [ $categories = [
"Aniki!", "Aniki!",
@ -140,11 +140,11 @@ class TestFeed extends \JKingWeb\Arsse\Test\AbstractTest {
"Bodybuilders", "Bodybuilders",
"Men", "Men",
]; ];
$this->assertSame([], $f->data->items[0]->categories); $this->assertSame([], $f->items[0]->categories);
$this->assertSame([], $f->data->items[1]->categories); $this->assertSame([], $f->items[1]->categories);
$this->assertSame([], $f->data->items[3]->categories); $this->assertSame([], $f->items[3]->categories);
$this->assertSame([], $f->data->items[4]->categories); $this->assertSame([], $f->items[4]->categories);
$this->assertSame($categories, $f->data->items[5]->categories); $this->assertSame($categories, $f->items[5]->categories);
} }
public function testDiscoverAFeedSuccessfully(): void { public function testDiscoverAFeedSuccessfully(): void {
@ -232,7 +232,7 @@ class TestFeed extends \JKingWeb\Arsse\Test\AbstractTest {
$e = "78567a"; $e = "78567a";
$f = new Feed(null, $this->base.$url."?t=$t&e=$e", Date::transform($t, "http"), $e); $f = new Feed(null, $this->base.$url."?t=$t&e=$e", Date::transform($t, "http"), $e);
$this->assertTime($t, $f->lastModified); $this->assertTime($t, $f->lastModified);
$this->assertSame($e, $f->resource->getETag()); $this->assertSame($e, $f->etag);
} }
public function provide304ResponseURLs() { public function provide304ResponseURLs() {
@ -250,15 +250,15 @@ class TestFeed extends \JKingWeb\Arsse\Test\AbstractTest {
$t = time() - 2000; $t = time() - 2000;
$f = new Feed(null, $this->base."Caching/200Past"); $f = new Feed(null, $this->base."Caching/200Past");
$this->assertTime($t, $f->lastModified); $this->assertTime($t, $f->lastModified);
$this->assertNotEmpty($f->resource->getETag()); $this->assertNotEmpty($f->etag);
$t = time() - 2000; $t = time() - 2000;
$f = new Feed(null, $this->base."Caching/200Past", Date::transform(time(), "http")); $f = new Feed(null, $this->base."Caching/200Past", Date::transform(time(), "http"));
$this->assertTime($t, $f->lastModified); $this->assertTime($t, $f->lastModified);
$this->assertNotEmpty($f->resource->getETag()); $this->assertNotEmpty($f->etag);
$t = time() + 2000; $t = time() + 2000;
$f = new Feed(null, $this->base."Caching/200Future"); $f = new Feed(null, $this->base."Caching/200Future");
$this->assertTime($t, $f->lastModified); $this->assertTime($t, $f->lastModified);
$this->assertNotEmpty($f->resource->getETag()); $this->assertNotEmpty($f->etag);
// these tests have no HTTP headers and rely on article dates // these tests have no HTTP headers and rely on article dates
$t = strtotime("2002-05-19T15:21:36Z"); $t = strtotime("2002-05-19T15:21:36Z");
$f = new Feed(null, $this->base."Caching/200PubDateOnly"); $f = new Feed(null, $this->base."Caching/200PubDateOnly");

View file

@ -15,6 +15,8 @@ use JKingWeb\Arsse\Test\Database;
class TestImportExport extends \JKingWeb\Arsse\Test\AbstractTest { class TestImportExport extends \JKingWeb\Arsse\Test\AbstractTest {
protected $drv; protected $drv;
protected $proc; protected $proc;
protected $data;
protected $primed;
protected $checkTables = [ protected $checkTables = [
'arsse_folders' => ["id", "owner", "parent", "name"], 'arsse_folders' => ["id", "owner", "parent", "name"],
'arsse_feeds' => ["id", "url", "title"], 'arsse_feeds' => ["id", "url", "title"],

View file

@ -39,6 +39,7 @@ class TestDaemon extends \JKingWeb\Arsse\Test\AbstractTest {
'unwritable' => "", // this file will be chmodded by the test 'unwritable' => "", // this file will be chmodded by the test
], ],
]; ];
protected $daemon;
public function setUp(): void { public function setUp(): void {
parent::setUp(); parent::setUp();

View file

@ -17,6 +17,8 @@ use JKingWeb\Arsse\User\Driver;
/** @covers \JKingWeb\Arsse\User */ /** @covers \JKingWeb\Arsse\User */
class TestUser extends \JKingWeb\Arsse\Test\AbstractTest { class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
protected $drv;
public function setUp(): void { public function setUp(): void {
parent::setUp(); parent::setUp();
self::setConf(); self::setConf();

View file

@ -615,16 +615,16 @@
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "9.2.23", "version": "9.2.24",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c" "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2cf940ebc6355a9d430462811b5aaa308b174bed",
"reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c", "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -680,7 +680,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.23" "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.24"
}, },
"funding": [ "funding": [
{ {
@ -688,7 +688,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2022-12-28T12:41:10+00:00" "time": "2023-01-26T08:26:55+00:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",