mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Merge changes from master
This commit is contained in:
parent
4e3369cd03
commit
8c6c49d588
10 changed files with 670 additions and 296 deletions
190
composer.lock
generated
190
composer.lock
generated
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "d00fd63e825db5ce16878c1639f362f3",
|
"content-hash": "1193e4106b6c84c545e6091560214ad5",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "docopt/docopt",
|
"name": "docopt/docopt",
|
||||||
|
@ -760,16 +760,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "friendsofphp/php-cs-fixer",
|
"name": "friendsofphp/php-cs-fixer",
|
||||||
"version": "v2.2.7",
|
"version": "v2.2.8",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git",
|
"url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git",
|
||||||
"reference": "b6202ccad4c00778887e7e8282d52f854802b59a"
|
"reference": "aca23e791784eade7b377d578d6dfc6fcf1398d2"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/b6202ccad4c00778887e7e8282d52f854802b59a",
|
"url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/aca23e791784eade7b377d578d6dfc6fcf1398d2",
|
||||||
"reference": "b6202ccad4c00778887e7e8282d52f854802b59a",
|
"reference": "aca23e791784eade7b377d578d6dfc6fcf1398d2",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -778,7 +778,7 @@
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-tokenizer": "*",
|
"ext-tokenizer": "*",
|
||||||
"gecko-packages/gecko-php-unit": "^2.0",
|
"gecko-packages/gecko-php-unit": "^2.0",
|
||||||
"php": "^5.3.6 || >=7.0 <7.2",
|
"php": "^5.3.6 || >=7.0 <7.3",
|
||||||
"sebastian/diff": "^1.4",
|
"sebastian/diff": "^1.4",
|
||||||
"symfony/console": "^2.4 || ^3.0",
|
"symfony/console": "^2.4 || ^3.0",
|
||||||
"symfony/event-dispatcher": "^2.1 || ^3.0",
|
"symfony/event-dispatcher": "^2.1 || ^3.0",
|
||||||
|
@ -841,7 +841,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "A tool to automatically fix PHP code style",
|
"description": "A tool to automatically fix PHP code style",
|
||||||
"time": "2017-09-11T14:27:07+00:00"
|
"time": "2017-09-29T15:07:49+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "gecko-packages/gecko-php-unit",
|
"name": "gecko-packages/gecko-php-unit",
|
||||||
|
@ -1136,16 +1136,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jms/serializer",
|
"name": "jms/serializer",
|
||||||
"version": "1.8.1",
|
"version": "1.9.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/schmittjoh/serializer.git",
|
"url": "https://github.com/schmittjoh/serializer.git",
|
||||||
"reference": "ce65798f722c836f16d5d7d2e3ca9d21e0fb4331"
|
"reference": "f4683f41ebf21e60667447bb49939bee35807c3c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/schmittjoh/serializer/zipball/ce65798f722c836f16d5d7d2e3ca9d21e0fb4331",
|
"url": "https://api.github.com/repos/schmittjoh/serializer/zipball/f4683f41ebf21e60667447bb49939bee35807c3c",
|
||||||
"reference": "ce65798f722c836f16d5d7d2e3ca9d21e0fb4331",
|
"reference": "f4683f41ebf21e60667447bb49939bee35807c3c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -1184,7 +1184,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "1.8-dev"
|
"dev-master": "1.9-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -1215,7 +1215,7 @@
|
||||||
"serialization",
|
"serialization",
|
||||||
"xml"
|
"xml"
|
||||||
],
|
],
|
||||||
"time": "2017-07-13T11:23:56+00:00"
|
"time": "2017-09-28T15:17:28+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "justinrainbow/json-schema",
|
"name": "justinrainbow/json-schema",
|
||||||
|
@ -1539,16 +1539,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "paragonie/random_compat",
|
"name": "paragonie/random_compat",
|
||||||
"version": "v2.0.10",
|
"version": "v2.0.11",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/paragonie/random_compat.git",
|
"url": "https://github.com/paragonie/random_compat.git",
|
||||||
"reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d"
|
"reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d",
|
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8",
|
||||||
"reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d",
|
"reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -1583,7 +1583,7 @@
|
||||||
"pseudorandom",
|
"pseudorandom",
|
||||||
"random"
|
"random"
|
||||||
],
|
],
|
||||||
"time": "2017-03-13T16:27:32+00:00"
|
"time": "2017-09-27T21:40:39+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pear/archive_tar",
|
"name": "pear/archive_tar",
|
||||||
|
@ -3632,16 +3632,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/config",
|
"name": "symfony/config",
|
||||||
"version": "v2.8.27",
|
"version": "v2.8.28",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/config.git",
|
"url": "https://github.com/symfony/config.git",
|
||||||
"reference": "0b8541d18507d10204a08384640ff6df3c739ebe"
|
"reference": "1dbeaa8e2db4b29159265867efff075ad961558c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/config/zipball/0b8541d18507d10204a08384640ff6df3c739ebe",
|
"url": "https://api.github.com/repos/symfony/config/zipball/1dbeaa8e2db4b29159265867efff075ad961558c",
|
||||||
"reference": "0b8541d18507d10204a08384640ff6df3c739ebe",
|
"reference": "1dbeaa8e2db4b29159265867efff075ad961558c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -3684,20 +3684,20 @@
|
||||||
],
|
],
|
||||||
"description": "Symfony Config Component",
|
"description": "Symfony Config Component",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"time": "2017-04-12T14:07:15+00:00"
|
"time": "2017-10-04T18:56:36+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/console",
|
"name": "symfony/console",
|
||||||
"version": "v2.8.27",
|
"version": "v2.8.28",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/console.git",
|
"url": "https://github.com/symfony/console.git",
|
||||||
"reference": "c0807a2ca978e64d8945d373a9221a5c35d1a253"
|
"reference": "f81549d2c5fdee8d711c9ab3c7e7362353ea5853"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/console/zipball/c0807a2ca978e64d8945d373a9221a5c35d1a253",
|
"url": "https://api.github.com/repos/symfony/console/zipball/f81549d2c5fdee8d711c9ab3c7e7362353ea5853",
|
||||||
"reference": "c0807a2ca978e64d8945d373a9221a5c35d1a253",
|
"reference": "f81549d2c5fdee8d711c9ab3c7e7362353ea5853",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -3745,7 +3745,7 @@
|
||||||
],
|
],
|
||||||
"description": "Symfony Console Component",
|
"description": "Symfony Console Component",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"time": "2017-08-27T14:29:03+00:00"
|
"time": "2017-10-01T21:00:16+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/debug",
|
"name": "symfony/debug",
|
||||||
|
@ -3806,16 +3806,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/event-dispatcher",
|
"name": "symfony/event-dispatcher",
|
||||||
"version": "v2.8.27",
|
"version": "v2.8.28",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||||
"reference": "1377400fd641d7d1935981546aaef780ecd5bf6d"
|
"reference": "7fe089232554357efb8d4af65ce209fc6e5a2186"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/1377400fd641d7d1935981546aaef780ecd5bf6d",
|
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/7fe089232554357efb8d4af65ce209fc6e5a2186",
|
||||||
"reference": "1377400fd641d7d1935981546aaef780ecd5bf6d",
|
"reference": "7fe089232554357efb8d4af65ce209fc6e5a2186",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -3862,7 +3862,7 @@
|
||||||
],
|
],
|
||||||
"description": "Symfony EventDispatcher Component",
|
"description": "Symfony EventDispatcher Component",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"time": "2017-06-02T07:47:27+00:00"
|
"time": "2017-10-01T21:00:16+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/filesystem",
|
"name": "symfony/filesystem",
|
||||||
|
@ -3915,16 +3915,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/finder",
|
"name": "symfony/finder",
|
||||||
"version": "v2.8.27",
|
"version": "v2.8.28",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/finder.git",
|
"url": "https://github.com/symfony/finder.git",
|
||||||
"reference": "4f4e84811004e065a3bb5ceeb1d9aa592630f9ad"
|
"reference": "a945724b201f74d543e356f6059c930bb8d10c92"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/finder/zipball/4f4e84811004e065a3bb5ceeb1d9aa592630f9ad",
|
"url": "https://api.github.com/repos/symfony/finder/zipball/a945724b201f74d543e356f6059c930bb8d10c92",
|
||||||
"reference": "4f4e84811004e065a3bb5ceeb1d9aa592630f9ad",
|
"reference": "a945724b201f74d543e356f6059c930bb8d10c92",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -3960,11 +3960,11 @@
|
||||||
],
|
],
|
||||||
"description": "Symfony Finder Component",
|
"description": "Symfony Finder Component",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"time": "2017-06-01T20:52:29+00:00"
|
"time": "2017-10-01T21:00:16+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/options-resolver",
|
"name": "symfony/options-resolver",
|
||||||
"version": "v3.3.9",
|
"version": "v3.3.10",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/options-resolver.git",
|
"url": "https://github.com/symfony/options-resolver.git",
|
||||||
|
@ -4018,16 +4018,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-mbstring",
|
"name": "symfony/polyfill-mbstring",
|
||||||
"version": "v1.5.0",
|
"version": "v1.6.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
"reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803"
|
"reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7c8fae0ac1d216eb54349e6a8baa57d515fe8803",
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
|
||||||
"reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803",
|
"reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -4039,7 +4039,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "1.5-dev"
|
"dev-master": "1.6-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -4073,20 +4073,20 @@
|
||||||
"portable",
|
"portable",
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"time": "2017-06-14T15:44:48+00:00"
|
"time": "2017-10-11T12:05:26+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php54",
|
"name": "symfony/polyfill-php54",
|
||||||
"version": "v1.5.0",
|
"version": "v1.6.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php54.git",
|
"url": "https://github.com/symfony/polyfill-php54.git",
|
||||||
"reference": "b7763422a5334c914ef0298ed21b253d25913a6e"
|
"reference": "d7810a14b2c6c1aff415e1bb755f611b3d5327bc"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/b7763422a5334c914ef0298ed21b253d25913a6e",
|
"url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/d7810a14b2c6c1aff415e1bb755f611b3d5327bc",
|
||||||
"reference": "b7763422a5334c914ef0298ed21b253d25913a6e",
|
"reference": "d7810a14b2c6c1aff415e1bb755f611b3d5327bc",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -4095,7 +4095,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "1.5-dev"
|
"dev-master": "1.6-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -4131,20 +4131,20 @@
|
||||||
"portable",
|
"portable",
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"time": "2017-06-14T15:44:48+00:00"
|
"time": "2017-10-11T12:05:26+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php55",
|
"name": "symfony/polyfill-php55",
|
||||||
"version": "v1.5.0",
|
"version": "v1.6.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php55.git",
|
"url": "https://github.com/symfony/polyfill-php55.git",
|
||||||
"reference": "29b1381d66f16e0581aab0b9f678ccf073288f68"
|
"reference": "b64e7f0c37ecf144ecc16668936eef94e628fbfd"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/29b1381d66f16e0581aab0b9f678ccf073288f68",
|
"url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/b64e7f0c37ecf144ecc16668936eef94e628fbfd",
|
||||||
"reference": "29b1381d66f16e0581aab0b9f678ccf073288f68",
|
"reference": "b64e7f0c37ecf144ecc16668936eef94e628fbfd",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -4154,7 +4154,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "1.5-dev"
|
"dev-master": "1.6-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -4187,20 +4187,20 @@
|
||||||
"portable",
|
"portable",
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"time": "2017-06-14T15:44:48+00:00"
|
"time": "2017-10-11T12:05:26+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php70",
|
"name": "symfony/polyfill-php70",
|
||||||
"version": "v1.5.0",
|
"version": "v1.6.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php70.git",
|
"url": "https://github.com/symfony/polyfill-php70.git",
|
||||||
"reference": "b6482e68974486984f59449ecea1fbbb22ff840f"
|
"reference": "0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/b6482e68974486984f59449ecea1fbbb22ff840f",
|
"url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff",
|
||||||
"reference": "b6482e68974486984f59449ecea1fbbb22ff840f",
|
"reference": "0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -4210,7 +4210,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "1.5-dev"
|
"dev-master": "1.6-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -4246,20 +4246,20 @@
|
||||||
"portable",
|
"portable",
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"time": "2017-06-14T15:44:48+00:00"
|
"time": "2017-10-11T12:05:26+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php72",
|
"name": "symfony/polyfill-php72",
|
||||||
"version": "v1.5.0",
|
"version": "v1.6.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php72.git",
|
"url": "https://github.com/symfony/polyfill-php72.git",
|
||||||
"reference": "8abc9097f5001d310f0edba727469c988acc6ea7"
|
"reference": "6de4f4884b97abbbed9f0a84a95ff2ff77254254"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/8abc9097f5001d310f0edba727469c988acc6ea7",
|
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/6de4f4884b97abbbed9f0a84a95ff2ff77254254",
|
||||||
"reference": "8abc9097f5001d310f0edba727469c988acc6ea7",
|
"reference": "6de4f4884b97abbbed9f0a84a95ff2ff77254254",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -4268,7 +4268,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "1.5-dev"
|
"dev-master": "1.6-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -4301,20 +4301,20 @@
|
||||||
"portable",
|
"portable",
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"time": "2017-07-11T13:25:55+00:00"
|
"time": "2017-10-11T12:05:26+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/process",
|
"name": "symfony/process",
|
||||||
"version": "v2.8.27",
|
"version": "v2.8.28",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/process.git",
|
"url": "https://github.com/symfony/process.git",
|
||||||
"reference": "57e52a0a6a80ea0aec4fc1b785a7920a95cb88a8"
|
"reference": "26c9fb02bf06bd6b90f661a5bd17e510810d0176"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/process/zipball/57e52a0a6a80ea0aec4fc1b785a7920a95cb88a8",
|
"url": "https://api.github.com/repos/symfony/process/zipball/26c9fb02bf06bd6b90f661a5bd17e510810d0176",
|
||||||
"reference": "57e52a0a6a80ea0aec4fc1b785a7920a95cb88a8",
|
"reference": "26c9fb02bf06bd6b90f661a5bd17e510810d0176",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -4350,20 +4350,20 @@
|
||||||
],
|
],
|
||||||
"description": "Symfony Process Component",
|
"description": "Symfony Process Component",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"time": "2017-07-03T08:04:30+00:00"
|
"time": "2017-10-01T21:00:16+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/stopwatch",
|
"name": "symfony/stopwatch",
|
||||||
"version": "v2.8.27",
|
"version": "v2.8.28",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/stopwatch.git",
|
"url": "https://github.com/symfony/stopwatch.git",
|
||||||
"reference": "e02577b841394a78306d7b547701bb7bb705bad5"
|
"reference": "28ee62ea4736431ca817cdaebcb005663e9cd1cb"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/stopwatch/zipball/e02577b841394a78306d7b547701bb7bb705bad5",
|
"url": "https://api.github.com/repos/symfony/stopwatch/zipball/28ee62ea4736431ca817cdaebcb005663e9cd1cb",
|
||||||
"reference": "e02577b841394a78306d7b547701bb7bb705bad5",
|
"reference": "28ee62ea4736431ca817cdaebcb005663e9cd1cb",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -4399,7 +4399,7 @@
|
||||||
],
|
],
|
||||||
"description": "Symfony Stopwatch Component",
|
"description": "Symfony Stopwatch Component",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"time": "2017-04-12T14:07:15+00:00"
|
"time": "2017-10-01T21:00:16+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/translation",
|
"name": "symfony/translation",
|
||||||
|
@ -4467,16 +4467,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/validator",
|
"name": "symfony/validator",
|
||||||
"version": "v2.8.27",
|
"version": "v2.8.28",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/validator.git",
|
"url": "https://github.com/symfony/validator.git",
|
||||||
"reference": "864ba6865e253a7ffc3db5629af676cfdc3bd104"
|
"reference": "1531ddfd96efd1b2c231cbf45f22e652a8f67925"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/validator/zipball/864ba6865e253a7ffc3db5629af676cfdc3bd104",
|
"url": "https://api.github.com/repos/symfony/validator/zipball/1531ddfd96efd1b2c231cbf45f22e652a8f67925",
|
||||||
"reference": "864ba6865e253a7ffc3db5629af676cfdc3bd104",
|
"reference": "1531ddfd96efd1b2c231cbf45f22e652a8f67925",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -4536,20 +4536,20 @@
|
||||||
],
|
],
|
||||||
"description": "Symfony Validator Component",
|
"description": "Symfony Validator Component",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"time": "2017-08-27T14:29:03+00:00"
|
"time": "2017-10-01T21:00:16+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/yaml",
|
"name": "symfony/yaml",
|
||||||
"version": "v3.3.9",
|
"version": "v3.3.10",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/yaml.git",
|
"url": "https://github.com/symfony/yaml.git",
|
||||||
"reference": "1d8c2a99c80862bdc3af94c1781bf70f86bccac0"
|
"reference": "8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/1d8c2a99c80862bdc3af94c1781bf70f86bccac0",
|
"url": "https://api.github.com/repos/symfony/yaml/zipball/8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46",
|
||||||
"reference": "1d8c2a99c80862bdc3af94c1781bf70f86bccac0",
|
"reference": "8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -4591,7 +4591,7 @@
|
||||||
],
|
],
|
||||||
"description": "Symfony Yaml Component",
|
"description": "Symfony Yaml Component",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"time": "2017-07-29T21:54:42+00:00"
|
"time": "2017-10-05T14:43:42+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "theseer/tokenizer",
|
"name": "theseer/tokenizer",
|
||||||
|
@ -4635,16 +4635,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "twig/twig",
|
"name": "twig/twig",
|
||||||
"version": "v1.34.4",
|
"version": "v1.35.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/twigphp/Twig.git",
|
"url": "https://github.com/twigphp/Twig.git",
|
||||||
"reference": "f878bab48edb66ad9c6ed626bf817f60c6c096ee"
|
"reference": "daa657073e55b0a78cce8fdd22682fddecc6385f"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/f878bab48edb66ad9c6ed626bf817f60c6c096ee",
|
"url": "https://api.github.com/repos/twigphp/Twig/zipball/daa657073e55b0a78cce8fdd22682fddecc6385f",
|
||||||
"reference": "f878bab48edb66ad9c6ed626bf817f60c6c096ee",
|
"reference": "daa657073e55b0a78cce8fdd22682fddecc6385f",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -4658,7 +4658,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "1.34-dev"
|
"dev-master": "1.35-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -4696,7 +4696,7 @@
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"templating"
|
"templating"
|
||||||
],
|
],
|
||||||
"time": "2017-07-04T13:19:31+00:00"
|
"time": "2017-09-27T18:06:46+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "webmozart/assert",
|
"name": "webmozart/assert",
|
||||||
|
|
|
@ -5,6 +5,8 @@ namespace JKingWeb\Arsse;
|
||||||
abstract class AbstractException extends \Exception {
|
abstract class AbstractException extends \Exception {
|
||||||
const CODES = [ "Exception.uncoded" => -1,
|
const CODES = [ "Exception.uncoded" => -1,
|
||||||
"Exception.unknown" => 10000,
|
"Exception.unknown" => 10000,
|
||||||
|
"ExceptionType.strictFailure" => 10011,
|
||||||
|
"ExceptionType.typeUnknown" => 10012,
|
||||||
"Lang/Exception.defaultFileMissing" => 10101,
|
"Lang/Exception.defaultFileMissing" => 10101,
|
||||||
"Lang/Exception.fileMissing" => 10102,
|
"Lang/Exception.fileMissing" => 10102,
|
||||||
"Lang/Exception.fileUnreadable" => 10103,
|
"Lang/Exception.fileUnreadable" => 10103,
|
||||||
|
|
6
lib/ExceptionType.php
Normal file
6
lib/ExceptionType.php
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\Arsse;
|
||||||
|
|
||||||
|
class ExceptionType extends AbstractException {
|
||||||
|
}
|
|
@ -3,6 +3,18 @@ declare(strict_types=1);
|
||||||
namespace JKingWeb\Arsse\Misc;
|
namespace JKingWeb\Arsse\Misc;
|
||||||
|
|
||||||
class Date {
|
class Date {
|
||||||
|
const FORMAT = [ // 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
|
||||||
|
'http' => ["!D, d M Y H:i:s \G\M\T", "D, d M Y H:i:s \G\M\T"],
|
||||||
|
'sql' => ["!Y-m-d H:i:s", "Y-m-d H:i:s" ],
|
||||||
|
'date' => ["!Y-m-d", "Y-m-d" ],
|
||||||
|
'time' => ["!H:i:s", "H:i:s" ],
|
||||||
|
'unix' => ["U", "U" ],
|
||||||
|
'float' => ["U.u", "U.u" ],
|
||||||
|
];
|
||||||
|
|
||||||
public static function transform($date, string $outFormat = null, string $inFormat = null, bool $inLocal = false) {
|
public static function transform($date, string $outFormat = null, string $inFormat = null, bool $inLocal = false) {
|
||||||
$date = self::normalize($date, $inFormat, $inLocal);
|
$date = self::normalize($date, $inFormat, $inLocal);
|
||||||
if (is_null($date) || is_null($outFormat)) {
|
if (is_null($date) || is_null($outFormat)) {
|
||||||
|
@ -23,41 +35,8 @@ class Date {
|
||||||
return $date->format($f);
|
return $date->format($f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function normalize($date, string $inFormat = null, bool $inLocal = false) {
|
public static function normalize($date, string $inFormat = null) {
|
||||||
if ($date instanceof \DateTimeInterface) {
|
return ValueInfo::normalize($date, ValueInfo::T_DATE, $inFormat);
|
||||||
return $date;
|
|
||||||
} elseif (is_numeric($date)) {
|
|
||||||
$time = (int) $date;
|
|
||||||
} elseif ($date===null) {
|
|
||||||
return null;
|
|
||||||
} elseif (is_string($date)) {
|
|
||||||
try {
|
|
||||||
$tz = (!$inLocal) ? new \DateTimeZone("UTC") : null;
|
|
||||||
if (!is_null($inFormat)) {
|
|
||||||
switch ($inFormat) {
|
|
||||||
case 'http': $f = "D, d M Y H:i:s \G\M\T"; break;
|
|
||||||
case 'iso8601': $f = "Y-m-d\TH:i:sP"; break;
|
|
||||||
case 'sql': $f = "Y-m-d H:i:s"; break;
|
|
||||||
case 'date': $f = "Y-m-d"; break;
|
|
||||||
case 'time': $f = "H:i:s"; break;
|
|
||||||
default: $f = $inFormat; break;
|
|
||||||
}
|
|
||||||
return \DateTime::createFromFormat("!".$f, $date, $tz);
|
|
||||||
} else {
|
|
||||||
return new \DateTime($date, $tz);
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} elseif (is_bool($date)) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
$time = (int) $date;
|
|
||||||
}
|
|
||||||
$tz = (!$inLocal) ? new \DateTimeZone("UTC") : null;
|
|
||||||
$d = new \DateTime("now", $tz);
|
|
||||||
$d->setTimestamp($time);
|
|
||||||
return $d;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function add(string $interval, $date = null): \DateTimeInterface {
|
public static function add(string $interval, $date = null): \DateTimeInterface {
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
namespace JKingWeb\Arsse\Misc;
|
namespace JKingWeb\Arsse\Misc;
|
||||||
|
|
||||||
|
use JKingWeb\Arsse\ExceptionType;
|
||||||
|
|
||||||
class ValueInfo {
|
class ValueInfo {
|
||||||
// universal
|
// universal
|
||||||
const VALID = 1 << 0;
|
const VALID = 1 << 0;
|
||||||
|
@ -9,9 +11,232 @@ class ValueInfo {
|
||||||
// integers
|
// integers
|
||||||
const ZERO = 1 << 2;
|
const ZERO = 1 << 2;
|
||||||
const NEG = 1 << 3;
|
const NEG = 1 << 3;
|
||||||
|
const FLOAT = 1 << 4;
|
||||||
// strings
|
// strings
|
||||||
const EMPTY = 1 << 2;
|
const EMPTY = 1 << 2;
|
||||||
const WHITE = 1 << 3;
|
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
|
||||||
|
//normalization modes
|
||||||
|
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 function normalize($value, int $type, string $dateFormat = null) {
|
||||||
|
$allowNull = ($type & self::M_NULL);
|
||||||
|
$strict = ($type & (self::M_STRICT | self::M_DROP));
|
||||||
|
$drop = ($type & self::M_DROP);
|
||||||
|
$arrayVal = ($type & self::M_ARRAY);
|
||||||
|
$type = ($type & ~(self::M_NULL | self::M_DROP | self::M_STRICT | self::M_ARRAY));
|
||||||
|
// if the value is null and this is allowed, simply return
|
||||||
|
if ($allowNull && is_null($value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// if the value is supposed to be an array, handle it specially
|
||||||
|
if ($arrayVal) {
|
||||||
|
$value = self::normalize($value, self::T_ARRAY);
|
||||||
|
foreach ($value as $key => $v) {
|
||||||
|
$value[$key] = self::normalize($v, $type | ($allowNull ? self::M_NULL : 0) | ($strict ? self::M_STRICT : 0) | ($drop ? self::M_DROP : 0), $dateFormat);
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
switch ($type) {
|
||||||
|
case self::T_MIXED:
|
||||||
|
return $value;
|
||||||
|
case self::T_NULL:
|
||||||
|
return null;
|
||||||
|
case self::T_BOOL:
|
||||||
|
if (is_bool($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
$out = self::bool($value);
|
||||||
|
if ($strict && is_null($out)) {
|
||||||
|
// if strict and input is not a boolean, this is an error
|
||||||
|
if ($drop) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
throw new ExceptionType("strictFailure", $type);
|
||||||
|
} elseif (is_float($value) && is_nan($value)) {
|
||||||
|
return false;
|
||||||
|
} elseif (is_null($out)) {
|
||||||
|
// if not strict and input is not a boolean, return a simple type-cast
|
||||||
|
return (bool) $value;
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
case self::T_INT:
|
||||||
|
if (is_int($value)) {
|
||||||
|
return $value;
|
||||||
|
} elseif ($value instanceof \DateTimeInterface) {
|
||||||
|
if ($strict && !$drop) {
|
||||||
|
throw new ExceptionType("strictFailure", $type);
|
||||||
|
}
|
||||||
|
return (!$drop) ? (int) $value->getTimestamp(): null;
|
||||||
|
}
|
||||||
|
$info = self::int($value);
|
||||||
|
if ($strict && !($info & self::VALID)) {
|
||||||
|
// if strict and input is not an integer, this is an error
|
||||||
|
if ($drop) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
throw new ExceptionType("strictFailure", $type);
|
||||||
|
} elseif (is_bool($value)) {
|
||||||
|
return (int) $value;
|
||||||
|
} elseif ($info & (self::VALID | self::FLOAT)) {
|
||||||
|
$out = strtolower((string) $value);
|
||||||
|
if (strpos($out, "e")) {
|
||||||
|
return (int) (float) $out;
|
||||||
|
} else {
|
||||||
|
return (int) $out;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case self::T_FLOAT:
|
||||||
|
if (is_float($value)) {
|
||||||
|
return $value;
|
||||||
|
} elseif ($value instanceof \DateTimeInterface) {
|
||||||
|
if ($strict && !$drop) {
|
||||||
|
throw new ExceptionType("strictFailure", $type);
|
||||||
|
}
|
||||||
|
return (!$drop) ? (float) $value->getTimestamp(): null;
|
||||||
|
} elseif (is_bool($value) && $strict) {
|
||||||
|
if ($drop) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
throw new ExceptionType("strictFailure", $type);
|
||||||
|
}
|
||||||
|
$out = filter_var($value, \FILTER_VALIDATE_FLOAT);
|
||||||
|
if ($strict && $out===false) {
|
||||||
|
// if strict and input is not a float, this is an error
|
||||||
|
if ($drop) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
throw new ExceptionType("strictFailure", $type);
|
||||||
|
}
|
||||||
|
return (float) $out;
|
||||||
|
case self::T_STRING:
|
||||||
|
if (is_string($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
if ($value instanceof \DateTimeImmutable) {
|
||||||
|
return $value->setTimezone(new \DateTimeZone("UTC"))->format(Date::FORMAT['iso8601'][1]);
|
||||||
|
} elseif ($value instanceof \DateTime) {
|
||||||
|
$out = clone $value;
|
||||||
|
$out->setTimezone(new \DateTimeZone("UTC"));
|
||||||
|
return $out->format(Date::FORMAT['iso8601'][1]);
|
||||||
|
} elseif (is_float($value) && is_finite($value)) {
|
||||||
|
$out = (string) $value;
|
||||||
|
if(!strpos($out, "E")) {
|
||||||
|
return $out;
|
||||||
|
} else {
|
||||||
|
$out = sprintf("%F", $value);
|
||||||
|
return substr($out, -2)==".0" ? (string) (int) $out : $out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$info = self::str($value);
|
||||||
|
if (!($info & self::VALID)) {
|
||||||
|
if ($drop) {
|
||||||
|
return null;
|
||||||
|
} elseif ($strict) {
|
||||||
|
// if strict and input is not a string, this is an error
|
||||||
|
throw new ExceptionType("strictFailure", $type);
|
||||||
|
} elseif (!is_scalar($value)) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
case self::T_DATE:
|
||||||
|
if ($value instanceof \DateTimeImmutable) {
|
||||||
|
return $value->setTimezone(new \DateTimeZone("UTC"));
|
||||||
|
} elseif ($value instanceof \DateTime) {
|
||||||
|
$out = clone $value;
|
||||||
|
$out->setTimezone(new \DateTimeZone("UTC"));
|
||||||
|
return $out;
|
||||||
|
} elseif (is_int($value)) {
|
||||||
|
return \DateTime::createFromFormat("U", (string) $value, new \DateTimeZone("UTC"));
|
||||||
|
} elseif (is_float($value)) {
|
||||||
|
return \DateTime::createFromFormat("U.u", sprintf("%F", $value), new \DateTimeZone("UTC"));
|
||||||
|
} elseif (is_string($value)) {
|
||||||
|
try {
|
||||||
|
if (!is_null($dateFormat)) {
|
||||||
|
$out = false;
|
||||||
|
if ($dateFormat=="microtime") {
|
||||||
|
// PHP is not able to correctly handle the output of microtime() as the input of DateTime::createFromFormat(), so we fudge it to look like a float
|
||||||
|
if (preg_match("<^0\.\d{6}00 \d+$>", $value)) {
|
||||||
|
$value = substr($value,11).".".substr($value,2,6);
|
||||||
|
} else {
|
||||||
|
throw new \Exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$f = isset(Date::FORMAT[$dateFormat]) ? Date::FORMAT[$dateFormat][0] : $dateFormat;
|
||||||
|
if ($dateFormat=="iso8601" || $dateFormat=="iso8601m") {
|
||||||
|
// DateTime::createFromFormat() doesn't provide one catch-all for ISO 8601 timezone specifiers, so we try all of them till one works
|
||||||
|
if ($dateFormat=="iso8601m") {
|
||||||
|
$f2 = Date::FORMAT["iso8601"][0];
|
||||||
|
$zones = [$f."", $f."\Z", $f."P", $f."O", $f2."", $f2."\Z", $f2."P", $f2."O"];
|
||||||
|
} else {
|
||||||
|
$zones = [$f."", $f."\Z", $f."P", $f."O"];
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
$ftz = array_shift($zones);
|
||||||
|
$out = \DateTime::createFromFormat($ftz, $value, new \DateTimeZone("UTC"));
|
||||||
|
} while (!$out && $zones);
|
||||||
|
} else {
|
||||||
|
$out = \DateTime::createFromFormat($f, $value, new \DateTimeZone("UTC"));
|
||||||
|
}
|
||||||
|
if (!$out) {
|
||||||
|
throw new \Exception;
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
} else {
|
||||||
|
return new \DateTime($value, new \DateTimeZone("UTC"));
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
if ($strict && !$drop) {
|
||||||
|
throw new ExceptionType("strictFailure", $type);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} elseif ($strict && !$drop) {
|
||||||
|
throw new ExceptionType("strictFailure", $type);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
case self::T_ARRAY:
|
||||||
|
if (is_array($value)) {
|
||||||
|
return $value;
|
||||||
|
} elseif ($value instanceof \Traversable) {
|
||||||
|
$out = [];
|
||||||
|
foreach ($value as $k => $v) {
|
||||||
|
$out[$k] = $v;
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
} else {
|
||||||
|
if ($drop) {
|
||||||
|
return null;
|
||||||
|
} elseif ($strict) {
|
||||||
|
// if strict and input is not a string, this is an error
|
||||||
|
throw new ExceptionType("strictFailure", $type);
|
||||||
|
} elseif (is_null($value) || (is_float($value) && is_nan($value))) {
|
||||||
|
return [];
|
||||||
|
} else {
|
||||||
|
return [$value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new ExceptionType("typeUnknown", $type); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function int($value): int {
|
public static function int($value): int {
|
||||||
$out = 0;
|
$out = 0;
|
||||||
|
@ -19,28 +244,42 @@ class ValueInfo {
|
||||||
// check if the input is null
|
// check if the input is null
|
||||||
return self::NULL;
|
return self::NULL;
|
||||||
} elseif (is_string($value) || (is_object($value) && method_exists($value, "__toString"))) {
|
} elseif (is_string($value) || (is_object($value) && method_exists($value, "__toString"))) {
|
||||||
$value = (string) $value;
|
$value = strtolower((string) $value);
|
||||||
// normalize a string an integer or float if possible
|
// normalize a string an integer or float if possible
|
||||||
if (!strlen($value)) {
|
if (!strlen($value)) {
|
||||||
// the empty string is equivalent to null when evaluating an integer
|
// the empty string is equivalent to null when evaluating an integer
|
||||||
return self::NULL;
|
return self::NULL;
|
||||||
} elseif (filter_var($value, \FILTER_VALIDATE_FLOAT) !== false && !fmod((float) $value, 1)) {
|
}
|
||||||
|
// interpret the value as a float
|
||||||
|
$float = filter_var($value, \FILTER_VALIDATE_FLOAT);
|
||||||
|
if ($float !== false) {
|
||||||
|
if (!fmod($float, 1)) {
|
||||||
// an integral float is acceptable
|
// an integral float is acceptable
|
||||||
$value = (int) $value;
|
$value = (int) (!strpos($value, "e") ? $value : $float);
|
||||||
|
} else {
|
||||||
|
$out += self::FLOAT;
|
||||||
|
$value = $float;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
} elseif (is_float($value) && !fmod($value, 1)) {
|
} elseif (is_float($value)) {
|
||||||
|
if (!fmod($value, 1)) {
|
||||||
// an integral float is acceptable
|
// an integral float is acceptable
|
||||||
$value = (int) $value;
|
$value = (int) $value;
|
||||||
|
} else {
|
||||||
|
$out += self::FLOAT;
|
||||||
|
}
|
||||||
} elseif (!is_int($value)) {
|
} elseif (!is_int($value)) {
|
||||||
// if the value is not an integer or integral float, stop
|
// if the value is not an integer or integral float, stop
|
||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
// mark validity
|
// mark validity
|
||||||
|
if (is_int($value)) {
|
||||||
$out += self::VALID;
|
$out += self::VALID;
|
||||||
|
}
|
||||||
// mark zeroness
|
// mark zeroness
|
||||||
if ($value==0) {
|
if (!$value) {
|
||||||
$out += self::ZERO;
|
$out += self::ZERO;
|
||||||
}
|
}
|
||||||
// mark negativeness
|
// mark negativeness
|
||||||
|
@ -96,8 +335,8 @@ class ValueInfo {
|
||||||
}
|
}
|
||||||
$out = filter_var($value, \FILTER_VALIDATE_BOOLEAN, \FILTER_NULL_ON_FAILURE);
|
$out = filter_var($value, \FILTER_VALIDATE_BOOLEAN, \FILTER_NULL_ON_FAILURE);
|
||||||
if (is_null($out) && (ValueInfo::int($value) & ValueInfo::VALID)) {
|
if (is_null($out) && (ValueInfo::int($value) & ValueInfo::VALID)) {
|
||||||
$out = abs((int) filter_var($value, \FILTER_VALIDATE_FLOAT));
|
$out = (int) filter_var($value, \FILTER_VALIDATE_FLOAT);
|
||||||
return ($out < 2) ? (bool) $out : $default;
|
return ($out==1 || $out==0) ? (bool) $out : $default;
|
||||||
}
|
}
|
||||||
return !is_null($out) ? $out : $default;
|
return !is_null($out) ? $out : $default;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,52 +32,13 @@ abstract class AbstractHandler implements Handler {
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function NormalizeInput(array $data, array $types, string $dateFormat = null): array {
|
protected function normalizeInput(array $data, array $types, string $dateFormat = null, int $mode = 0): array {
|
||||||
$out = [];
|
$out = [];
|
||||||
foreach ($data as $key => $value) {
|
foreach ($types as $key => $type) {
|
||||||
if (!isset($types[$key])) {
|
if (isset($data[$key])) {
|
||||||
$out[$key] = $value;
|
$out[$key] = ValueInfo::normalize($data[$key], $type | $mode, $dateFormat);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (is_null($value)) {
|
|
||||||
$out[$key] = null;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
switch ($types[$key]) {
|
|
||||||
case "int":
|
|
||||||
if (valueInfo::int($value) & ValueInfo::VALID) {
|
|
||||||
$out[$key] = (int) $value;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "string":
|
|
||||||
if (is_bool($value)) {
|
|
||||||
$out[$key] = var_export($value, true);
|
|
||||||
} elseif (!is_scalar($value)) {
|
|
||||||
break;
|
|
||||||
} else {
|
} else {
|
||||||
$out[$key] = (string) $value;
|
$out[$key] = null;
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "bool":
|
|
||||||
$test = ValueInfo::bool($value);
|
|
||||||
if (!is_null($test)) {
|
|
||||||
$out[$key] = $test;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "float":
|
|
||||||
$test = filter_var($value, \FILTER_VALIDATE_FLOAT);
|
|
||||||
if ($test !== false) {
|
|
||||||
$out[$key] = $test;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "datetime":
|
|
||||||
$t = Date::normalize($value, $dateFormat);
|
|
||||||
if ($t) {
|
|
||||||
$out[$key] = $t;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("typeUnknown", $types[$key]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $out;
|
return $out;
|
||||||
|
|
|
@ -21,21 +21,21 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
protected $dateFormat = "unix";
|
protected $dateFormat = "unix";
|
||||||
|
|
||||||
protected $validInput = [
|
protected $validInput = [
|
||||||
'name' => "string",
|
'name' => ValueInfo::T_STRING,
|
||||||
'url' => "string",
|
'url' => ValueInfo::T_STRING,
|
||||||
'folderId' => "int",
|
'folderId' => ValueInfo::T_INT,
|
||||||
'feedTitle' => "string",
|
'feedTitle' => ValueInfo::T_STRING,
|
||||||
'userId' => "string",
|
'userId' => ValueInfo::T_STRING,
|
||||||
'feedId' => "int",
|
'feedId' => ValueInfo::T_INT,
|
||||||
'newestItemId' => "int",
|
'newestItemId' => ValueInfo::T_INT,
|
||||||
'batchSize' => "int",
|
'batchSize' => ValueInfo::T_INT,
|
||||||
'offset' => "int",
|
'offset' => ValueInfo::T_INT,
|
||||||
'type' => "int",
|
'type' => ValueInfo::T_INT,
|
||||||
'id' => "int",
|
'id' => ValueInfo::T_INT,
|
||||||
'getRead' => "bool",
|
'getRead' => ValueInfo::T_BOOL,
|
||||||
'oldestFirst' => "bool",
|
'oldestFirst' => ValueInfo::T_BOOL,
|
||||||
'lastModified' => "datetime",
|
'lastModified' => ValueInfo::T_DATE,
|
||||||
// 'items' => "array int", // just pass these through
|
'items' => ValueInfo::T_MIXED | ValueInfo::M_ARRAY,
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
|
@ -61,10 +61,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
$data = [];
|
$data = [];
|
||||||
}
|
}
|
||||||
// FIXME: Do query parameters take precedence in NextCloud? Is there a conflict error when values differ?
|
// FIXME: Do query parameters take precedence in NextCloud? Is there a conflict error when values differ?
|
||||||
$data = $this->normalizeInput($data, $this->validInput, "U");
|
$data = $this->normalizeInput(array_merge($data, $req->query), $this->validInput, "unix");
|
||||||
$query = $this->normalizeInput($req->query, $this->validInput, "U");
|
|
||||||
$data = array_merge($data, $query);
|
|
||||||
unset($query);
|
|
||||||
// check to make sure the requested function is implemented
|
// check to make sure the requested function is implemented
|
||||||
try {
|
try {
|
||||||
$func = $this->chooseCall($req->paths, $req->method);
|
$func = $this->chooseCall($req->paths, $req->method);
|
||||||
|
@ -233,7 +230,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
// create a folder
|
// create a folder
|
||||||
protected function folderAdd(array $url, array $data): Response {
|
protected function folderAdd(array $url, array $data): Response {
|
||||||
try {
|
try {
|
||||||
$folder = Arsse::$db->folderAdd(Arsse::$user->id, $data);
|
$folder = Arsse::$db->folderAdd(Arsse::$user->id, ['name' => $data['name']]);
|
||||||
} catch (ExceptionInput $e) {
|
} catch (ExceptionInput $e) {
|
||||||
switch ($e->getCode()) {
|
switch ($e->getCode()) {
|
||||||
// folder already exists
|
// folder already exists
|
||||||
|
@ -263,13 +260,8 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
|
|
||||||
// rename a folder (also supports moving nesting folders, but this is not a feature of the API)
|
// rename a folder (also supports moving nesting folders, but this is not a feature of the API)
|
||||||
protected function folderRename(array $url, array $data): Response {
|
protected function folderRename(array $url, array $data): Response {
|
||||||
// there must be some change to be made
|
|
||||||
if (!sizeof($data)) {
|
|
||||||
return new Response(422);
|
|
||||||
}
|
|
||||||
// perform the edit
|
|
||||||
try {
|
try {
|
||||||
Arsse::$db->folderPropertiesSet(Arsse::$user->id, (int) $url[1], $data);
|
Arsse::$db->folderPropertiesSet(Arsse::$user->id, (int) $url[1], ['name' => $data['name']]);
|
||||||
} catch (ExceptionInput $e) {
|
} catch (ExceptionInput $e) {
|
||||||
switch ($e->getCode()) {
|
switch ($e->getCode()) {
|
||||||
// folder does not exist
|
// folder does not exist
|
||||||
|
@ -288,15 +280,13 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
|
|
||||||
// mark all articles associated with a folder as read
|
// mark all articles associated with a folder as read
|
||||||
protected function folderMarkRead(array $url, array $data): Response {
|
protected function folderMarkRead(array $url, array $data): Response {
|
||||||
$c = new Context;
|
if (!ValueInfo::id($data['newestItemId'])) {
|
||||||
if (isset($data['newestItemId'])) {
|
// if the item ID is invalid (i.e. not a positive integer), this is an error
|
||||||
// if the item ID is valid (i.e. an integer), add it to the context
|
|
||||||
$c->latestEdition($data['newestItemId']);
|
|
||||||
} else {
|
|
||||||
// otherwise return an error
|
|
||||||
return new Response(422);
|
return new Response(422);
|
||||||
}
|
}
|
||||||
// add the folder ID to the context
|
// build the context
|
||||||
|
$c = new Context;
|
||||||
|
$c->latestEdition((int) $data['newestItemId']);
|
||||||
$c->folder((int) $url[1]);
|
$c->folder((int) $url[1]);
|
||||||
// perform the operation
|
// perform the operation
|
||||||
try {
|
try {
|
||||||
|
@ -330,10 +320,6 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
if (Arsse::$user->rightsGet(Arsse::$user->id)==User::RIGHTS_NONE) {
|
if (Arsse::$user->rightsGet(Arsse::$user->id)==User::RIGHTS_NONE) {
|
||||||
return new Response(403);
|
return new Response(403);
|
||||||
}
|
}
|
||||||
// perform an update of a single feed
|
|
||||||
if (!isset($data['feedId'])) {
|
|
||||||
return new Response(422);
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
Arsse::$db->feedUpdate($data['feedId']);
|
Arsse::$db->feedUpdate($data['feedId']);
|
||||||
} catch (ExceptionInput $e) {
|
} catch (ExceptionInput $e) {
|
||||||
|
@ -351,16 +337,10 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
|
|
||||||
// add a new feed
|
// add a new feed
|
||||||
protected function subscriptionAdd(array $url, array $data): Response {
|
protected function subscriptionAdd(array $url, array $data): Response {
|
||||||
// normalize the feed URL
|
|
||||||
if (!isset($data['url'])) {
|
|
||||||
return new Response(422);
|
|
||||||
}
|
|
||||||
// normalize the folder ID, if specified
|
|
||||||
$folder = isset($data['folderId']) ? $data['folderId'] : null;
|
|
||||||
// try to add the feed
|
// try to add the feed
|
||||||
$tr = Arsse::$db->begin();
|
$tr = Arsse::$db->begin();
|
||||||
try {
|
try {
|
||||||
$id = Arsse::$db->subscriptionAdd(Arsse::$user->id, $data['url']);
|
$id = Arsse::$db->subscriptionAdd(Arsse::$user->id, (string) $data['url']);
|
||||||
} catch (ExceptionInput $e) {
|
} catch (ExceptionInput $e) {
|
||||||
// feed already exists
|
// feed already exists
|
||||||
return new Response(409);
|
return new Response(409);
|
||||||
|
@ -369,9 +349,9 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
return new Response(422);
|
return new Response(422);
|
||||||
}
|
}
|
||||||
// if a folder was specified, move the feed to the correct folder; silently ignore errors
|
// if a folder was specified, move the feed to the correct folder; silently ignore errors
|
||||||
if ($folder) {
|
if ($data['folderId']) {
|
||||||
try {
|
try {
|
||||||
Arsse::$db->subscriptionPropertiesSet(Arsse::$user->id, $id, ['folder' => $folder]);
|
Arsse::$db->subscriptionPropertiesSet(Arsse::$user->id, $id, ['folder' => $data['folderId']]);
|
||||||
} catch (ExceptionInput $e) {
|
} catch (ExceptionInput $e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -416,16 +396,8 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
|
|
||||||
// rename a feed
|
// rename a feed
|
||||||
protected function subscriptionRename(array $url, array $data): Response {
|
protected function subscriptionRename(array $url, array $data): Response {
|
||||||
// normalize input
|
|
||||||
$in = [];
|
|
||||||
if (array_key_exists('feedTitle', $data)) { // we use array_key_exists because null is a valid input
|
|
||||||
$in['title'] = $data['feedTitle'];
|
|
||||||
} else {
|
|
||||||
return new Response(422);
|
|
||||||
}
|
|
||||||
// perform the renaming
|
|
||||||
try {
|
try {
|
||||||
Arsse::$db->subscriptionPropertiesSet(Arsse::$user->id, (int) $url[1], $in);
|
Arsse::$db->subscriptionPropertiesSet(Arsse::$user->id, (int) $url[1], ['title' => (string) $data['feedTitle']]);
|
||||||
} catch (ExceptionInput $e) {
|
} catch (ExceptionInput $e) {
|
||||||
switch ($e->getCode()) {
|
switch ($e->getCode()) {
|
||||||
// subscription does not exist
|
// subscription does not exist
|
||||||
|
@ -442,16 +414,13 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
|
|
||||||
// move a feed to a folder
|
// move a feed to a folder
|
||||||
protected function subscriptionMove(array $url, array $data): Response {
|
protected function subscriptionMove(array $url, array $data): Response {
|
||||||
// normalize input
|
// if no folder is specified this is an error
|
||||||
$in = [];
|
if (!isset($data['folderId'])) {
|
||||||
if (isset($data['folderId'])) {
|
|
||||||
$in['folder'] = $data['folderId'] ? $data['folderId'] : null;
|
|
||||||
} else {
|
|
||||||
return new Response(422);
|
return new Response(422);
|
||||||
}
|
}
|
||||||
// perform the move
|
// perform the move
|
||||||
try {
|
try {
|
||||||
Arsse::$db->subscriptionPropertiesSet(Arsse::$user->id, (int) $url[1], $in);
|
Arsse::$db->subscriptionPropertiesSet(Arsse::$user->id, (int) $url[1], ['folder' => $data['folderId']]);
|
||||||
} catch (ExceptionInput $e) {
|
} catch (ExceptionInput $e) {
|
||||||
switch ($e->getCode()) {
|
switch ($e->getCode()) {
|
||||||
case 10239: // subscription does not exist
|
case 10239: // subscription does not exist
|
||||||
|
@ -468,14 +437,13 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
|
|
||||||
// mark all articles associated with a subscription as read
|
// mark all articles associated with a subscription as read
|
||||||
protected function subscriptionMarkRead(array $url, array $data): Response {
|
protected function subscriptionMarkRead(array $url, array $data): Response {
|
||||||
$c = new Context;
|
if (!ValueInfo::id($data['newestItemId'])) {
|
||||||
if (isset($data['newestItemId'])) {
|
// if the item ID is invalid (i.e. not a positive integer), this is an error
|
||||||
$c->latestEdition($data['newestItemId']);
|
|
||||||
} else {
|
|
||||||
// otherwise return an error
|
|
||||||
return new Response(422);
|
return new Response(422);
|
||||||
}
|
}
|
||||||
// add the subscription ID to the context
|
// build the context
|
||||||
|
$c = new Context;
|
||||||
|
$c->latestEdition((int) $data['newestItemId']);
|
||||||
$c->subscription((int) $url[1]);
|
$c->subscription((int) $url[1]);
|
||||||
// perform the operation
|
// perform the operation
|
||||||
try {
|
try {
|
||||||
|
@ -492,17 +460,17 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
// set the context options supplied by the client
|
// set the context options supplied by the client
|
||||||
$c = new Context;
|
$c = new Context;
|
||||||
// set the batch size
|
// set the batch size
|
||||||
if (isset($data['batchSize']) && $data['batchSize'] > 0) {
|
if ($data['batchSize'] > 0) {
|
||||||
$c->limit($data['batchSize']);
|
$c->limit($data['batchSize']);
|
||||||
}
|
}
|
||||||
// set the order of returned items
|
// set the order of returned items
|
||||||
if (isset($data['oldestFirst']) && $data['oldestFirst']) {
|
if ($data['oldestFirst']) {
|
||||||
$c->reverse(false);
|
$c->reverse(false);
|
||||||
} else {
|
} else {
|
||||||
$c->reverse(true);
|
$c->reverse(true);
|
||||||
}
|
}
|
||||||
// set the edition mark-off; the database uses an or-equal comparison for internal consistency, but the protocol does not, so we must adjust by one
|
// set the edition mark-off; the database uses an or-equal comparison for internal consistency, but the protocol does not, so we must adjust by one
|
||||||
if (isset($data['offset']) && $data['offset'] > 0) {
|
if ($data['offset'] > 0) {
|
||||||
if ($c->reverse) {
|
if ($c->reverse) {
|
||||||
$c->latestEdition($data['offset'] - 1);
|
$c->latestEdition($data['offset'] - 1);
|
||||||
} else {
|
} else {
|
||||||
|
@ -510,13 +478,11 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// set whether to only return unread
|
// set whether to only return unread
|
||||||
if (isset($data['getRead']) && !$data['getRead']) {
|
if (!ValueInfo::bool($data['getRead'], true)) {
|
||||||
$c->unread(true);
|
$c->unread(true);
|
||||||
}
|
}
|
||||||
// if no type is specified assume 3 (All)
|
// if no type is specified assume 3 (All)
|
||||||
if (!isset($data['type'])) {
|
$data['type'] = $data['type'] ?? 3;
|
||||||
$data['type'] = 3;
|
|
||||||
}
|
|
||||||
switch ($data['type']) {
|
switch ($data['type']) {
|
||||||
case 0: // feed
|
case 0: // feed
|
||||||
if (isset($data['id'])) {
|
if (isset($data['id'])) {
|
||||||
|
@ -535,7 +501,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
// return all items
|
// return all items
|
||||||
}
|
}
|
||||||
// whether to return only updated items
|
// whether to return only updated items
|
||||||
if (isset($data['lastModified'])) {
|
if ($data['lastModified']) {
|
||||||
$c->modifiedSince($data['lastModified']);
|
$c->modifiedSince($data['lastModified']);
|
||||||
}
|
}
|
||||||
// perform the fetch
|
// perform the fetch
|
||||||
|
@ -555,14 +521,13 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
|
|
||||||
// mark all articles as read
|
// mark all articles as read
|
||||||
protected function articleMarkReadAll(array $url, array $data): Response {
|
protected function articleMarkReadAll(array $url, array $data): Response {
|
||||||
$c = new Context;
|
if (!ValueInfo::id($data['newestItemId'])) {
|
||||||
if (isset($data['newestItemId'])) {
|
// if the item ID is invalid (i.e. not a positive integer), this is an error
|
||||||
// set the newest item ID as specified
|
|
||||||
$c->latestEdition($data['newestItemId']);
|
|
||||||
} else {
|
|
||||||
// otherwise return an error
|
|
||||||
return new Response(422);
|
return new Response(422);
|
||||||
}
|
}
|
||||||
|
// build the context
|
||||||
|
$c = new Context;
|
||||||
|
$c->latestEdition((int) $data['newestItemId']);
|
||||||
// perform the operation
|
// perform the operation
|
||||||
Arsse::$db->articleMark(Arsse::$user->id, ['read' => true], $c);
|
Arsse::$db->articleMark(Arsse::$user->id, ['read' => true], $c);
|
||||||
return new Response(204);
|
return new Response(204);
|
||||||
|
@ -604,13 +569,9 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
protected function articleMarkReadMulti(array $url, array $data): Response {
|
protected function articleMarkReadMulti(array $url, array $data): Response {
|
||||||
// determine whether to mark read or unread
|
// determine whether to mark read or unread
|
||||||
$set = ($url[1]=="read");
|
$set = ($url[1]=="read");
|
||||||
// if the input data is not at all valid, return an error
|
|
||||||
if (!isset($data['items']) || !is_array($data['items'])) {
|
|
||||||
return new Response(422);
|
|
||||||
}
|
|
||||||
// start a transaction and loop through the items
|
// start a transaction and loop through the items
|
||||||
$t = Arsse::$db->begin();
|
$t = Arsse::$db->begin();
|
||||||
$in = array_chunk($data['items'], 50);
|
$in = array_chunk($data['items'] ?? [], 50);
|
||||||
for ($a = 0; $a < sizeof($in); $a++) {
|
for ($a = 0; $a < sizeof($in); $a++) {
|
||||||
// initialize the matching context
|
// initialize the matching context
|
||||||
$c = new Context;
|
$c = new Context;
|
||||||
|
@ -628,13 +589,9 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
protected function articleMarkStarredMulti(array $url, array $data): Response {
|
protected function articleMarkStarredMulti(array $url, array $data): Response {
|
||||||
// determine whether to mark starred or unstarred
|
// determine whether to mark starred or unstarred
|
||||||
$set = ($url[1]=="star");
|
$set = ($url[1]=="star");
|
||||||
// if the input data is not at all valid, return an error
|
|
||||||
if (!isset($data['items']) || !is_array($data['items'])) {
|
|
||||||
return new Response(422);
|
|
||||||
}
|
|
||||||
// start a transaction and loop through the items
|
// start a transaction and loop through the items
|
||||||
$t = Arsse::$db->begin();
|
$t = Arsse::$db->begin();
|
||||||
$in = array_chunk(array_column($data['items'], "guidHash"), 50);
|
$in = array_chunk(array_column($data['items'] ?? [], "guidHash"), 50);
|
||||||
for ($a = 0; $a < sizeof($in); $a++) {
|
for ($a = 0; $a < sizeof($in); $a++) {
|
||||||
// initialize the matching context
|
// initialize the matching context
|
||||||
$c = new Context;
|
$c = new Context;
|
||||||
|
|
|
@ -74,6 +74,17 @@ return [
|
||||||
'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',
|
||||||
// this should not usually be encountered
|
// this should not usually be encountered
|
||||||
'Exception.JKingWeb/Arsse/Exception.unknown' => 'An unknown error has occurred',
|
'Exception.JKingWeb/Arsse/Exception.unknown' => 'An unknown error has occurred',
|
||||||
|
'Exception.JKingWeb/Arsse/ExceptionType.strictFailure' => 'Supplied value could not be normalized to {0, select,
|
||||||
|
1 {null}
|
||||||
|
2 {boolean}
|
||||||
|
3 {integer}
|
||||||
|
4 {float}
|
||||||
|
5 {datetime}
|
||||||
|
6 {string}
|
||||||
|
7 {array}
|
||||||
|
other {requested type}
|
||||||
|
}',
|
||||||
|
'Exception.JKingWeb/Arsse/ExceptionType.typeUnknown' => 'Normalization type {0} is not implemented',
|
||||||
'Exception.JKingWeb/Arsse/Lang/Exception.defaultFileMissing' => 'Default language file "{0}" missing',
|
'Exception.JKingWeb/Arsse/Lang/Exception.defaultFileMissing' => 'Default language file "{0}" missing',
|
||||||
'Exception.JKingWeb/Arsse/Lang/Exception.fileMissing' => 'Language file "{0}" is not available',
|
'Exception.JKingWeb/Arsse/Lang/Exception.fileMissing' => 'Language file "{0}" is not available',
|
||||||
'Exception.JKingWeb/Arsse/Lang/Exception.fileUnreadable' => 'Insufficient permissions to read language file "{0}"',
|
'Exception.JKingWeb/Arsse/Lang/Exception.fileUnreadable' => 'Insufficient permissions to read language file "{0}"',
|
||||||
|
|
|
@ -7,6 +7,10 @@ use JKingWeb\Arsse\Test\Misc\StrClass;
|
||||||
|
|
||||||
/** @covers \JKingWeb\Arsse\Misc\ValueInfo */
|
/** @covers \JKingWeb\Arsse\Misc\ValueInfo */
|
||||||
class TestValueInfo extends Test\AbstractTest {
|
class TestValueInfo extends Test\AbstractTest {
|
||||||
|
public function setUp() {
|
||||||
|
$this->clearData();
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetIntegerInfo() {
|
public function testGetIntegerInfo() {
|
||||||
$tests = [
|
$tests = [
|
||||||
[null, I::NULL],
|
[null, I::NULL],
|
||||||
|
@ -58,9 +62,9 @@ class TestValueInfo extends Test\AbstractTest {
|
||||||
["no", 0],
|
["no", 0],
|
||||||
["true", 0],
|
["true", 0],
|
||||||
["false", 0],
|
["false", 0],
|
||||||
[INF, 0],
|
[INF, I::FLOAT],
|
||||||
[-INF, 0],
|
[-INF, I::FLOAT | I::NEG],
|
||||||
[NAN, 0],
|
[NAN, I::FLOAT],
|
||||||
[[], 0],
|
[[], 0],
|
||||||
["some string", 0],
|
["some string", 0],
|
||||||
[" ", 0],
|
[" ", 0],
|
||||||
|
@ -71,6 +75,10 @@ class TestValueInfo extends Test\AbstractTest {
|
||||||
[new StrClass("-1"), I::VALID | I::NEG],
|
[new StrClass("-1"), I::VALID | I::NEG],
|
||||||
[new StrClass("Msg"), 0],
|
[new StrClass("Msg"), 0],
|
||||||
[new StrClass(" "), 0],
|
[new StrClass(" "), 0],
|
||||||
|
[2.5, I::FLOAT],
|
||||||
|
[0.5, I::FLOAT],
|
||||||
|
["2.5", I::FLOAT],
|
||||||
|
["0.5", I::FLOAT],
|
||||||
];
|
];
|
||||||
foreach ($tests as $test) {
|
foreach ($tests as $test) {
|
||||||
list($value, $exp) = $test;
|
list($value, $exp) = $test;
|
||||||
|
@ -249,13 +257,13 @@ class TestValueInfo extends Test\AbstractTest {
|
||||||
["+000", false],
|
["+000", false],
|
||||||
["+0.0", false],
|
["+0.0", false],
|
||||||
["+000.000", false],
|
["+000.000", false],
|
||||||
[-1, true],
|
[-1, null],
|
||||||
[-1.0, true],
|
[-1.0, null],
|
||||||
["-1.0", true],
|
["-1.0", null],
|
||||||
["-001.0", true],
|
["-001.0", null],
|
||||||
["-1.0e2", null],
|
["-1.0e2", null],
|
||||||
["-1", true],
|
["-1", null],
|
||||||
["-001", true],
|
["-001", null],
|
||||||
["-1e2", null],
|
["-1e2", null],
|
||||||
[-0, false],
|
[-0, false],
|
||||||
["-0", false],
|
["-0", false],
|
||||||
|
@ -281,7 +289,7 @@ class TestValueInfo extends Test\AbstractTest {
|
||||||
[new StrClass(""), false],
|
[new StrClass(""), false],
|
||||||
[new StrClass("1"), true],
|
[new StrClass("1"), true],
|
||||||
[new StrClass("0"), false],
|
[new StrClass("0"), false],
|
||||||
[new StrClass("-1"), true],
|
[new StrClass("-1"), null],
|
||||||
[new StrClass("Msg"), null],
|
[new StrClass("Msg"), null],
|
||||||
[new StrClass(" "), null],
|
[new StrClass(" "), null],
|
||||||
];
|
];
|
||||||
|
@ -294,4 +302,215 @@ class TestValueInfo extends Test\AbstractTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testNormalizeValues() {
|
||||||
|
$tests = [
|
||||||
|
/* The test data are very dense for this set. Each value is normalized to each of the following types:
|
||||||
|
|
||||||
|
- mixed (no normalization performed)
|
||||||
|
- null
|
||||||
|
- boolean
|
||||||
|
- integer
|
||||||
|
- float
|
||||||
|
- string
|
||||||
|
- array
|
||||||
|
|
||||||
|
For each of these types, there is an expected output value, as well as a boolean indicating whether
|
||||||
|
the value should pass or fail a strict normalization. Conversion to DateTime is covered below by a different data set
|
||||||
|
*/
|
||||||
|
/* Input value null bool int float string array */
|
||||||
|
[null, [null,true], [false,false], [0, false], [0.0, false], ["", false], [[], false]],
|
||||||
|
["", [null,true], [false,true], [0, false], [0.0, false], ["", true], [[""], false]],
|
||||||
|
[1, [null,true], [true, true], [1, true], [1.0, true], ["1", true], [[1], false]],
|
||||||
|
[PHP_INT_MAX, [null,true], [true, false], [PHP_INT_MAX, true], [(float) PHP_INT_MAX,true], [(string) PHP_INT_MAX, true], [[PHP_INT_MAX], false]],
|
||||||
|
[1.0, [null,true], [true, true], [1, true], [1.0, true], ["1", true], [[1.0], false]],
|
||||||
|
["1.0", [null,true], [true, true], [1, true], [1.0, true], ["1.0", true], [["1.0"], false]],
|
||||||
|
["001.0", [null,true], [true, true], [1, true], [1.0, true], ["001.0", true], [["001.0"], false]],
|
||||||
|
["1.0e2", [null,true], [true, false], [100, true], [100.0, true], ["1.0e2", true], [["1.0e2"], false]],
|
||||||
|
["1", [null,true], [true, true], [1, true], [1.0, true], ["1", true], [["1"], false]],
|
||||||
|
["001", [null,true], [true, true], [1, true], [1.0, true], ["001", true], [["001"], false]],
|
||||||
|
["1e2", [null,true], [true, false], [100, true], [100.0, true], ["1e2", true], [["1e2"], false]],
|
||||||
|
["+1.0", [null,true], [true, true], [1, true], [1.0, true], ["+1.0", true], [["+1.0"], false]],
|
||||||
|
["+001.0", [null,true], [true, true], [1, true], [1.0, true], ["+001.0", true], [["+001.0"], false]],
|
||||||
|
["+1.0e2", [null,true], [true, false], [100, true], [100.0, true], ["+1.0e2", true], [["+1.0e2"], false]],
|
||||||
|
["+1", [null,true], [true, true], [1, true], [1.0, true], ["+1", true], [["+1"], false]],
|
||||||
|
["+001", [null,true], [true, true], [1, true], [1.0, true], ["+001", true], [["+001"], false]],
|
||||||
|
["+1e2", [null,true], [true, false], [100, true], [100.0, true], ["+1e2", true], [["+1e2"], false]],
|
||||||
|
[0, [null,true], [false,true], [0, true], [0.0, true], ["0", true], [[0], false]],
|
||||||
|
["0", [null,true], [false,true], [0, true], [0.0, true], ["0", true], [["0"], false]],
|
||||||
|
["000", [null,true], [false,true], [0, true], [0.0, true], ["000", true], [["000"], false]],
|
||||||
|
[0.0, [null,true], [false,true], [0, true], [0.0, true], ["0", true], [[0.0], false]],
|
||||||
|
["0.0", [null,true], [false,true], [0, true], [0.0, true], ["0.0", true], [["0.0"], false]],
|
||||||
|
["000.000", [null,true], [false,true], [0, true], [0.0, true], ["000.000", true], [["000.000"], false]],
|
||||||
|
["+0", [null,true], [false,true], [0, true], [0.0, true], ["+0", true], [["+0"], false]],
|
||||||
|
["+000", [null,true], [false,true], [0, true], [0.0, true], ["+000", true], [["+000"], false]],
|
||||||
|
["+0.0", [null,true], [false,true], [0, true], [0.0, true], ["+0.0", true], [["+0.0"], false]],
|
||||||
|
["+000.000", [null,true], [false,true], [0, true], [0.0, true], ["+000.000", true], [["+000.000"], false]],
|
||||||
|
[-1, [null,true], [true, false], [-1, true], [-1.0, true], ["-1", true], [[-1], false]],
|
||||||
|
[-1.0, [null,true], [true, false], [-1, true], [-1.0, true], ["-1", true], [[-1.0], false]],
|
||||||
|
["-1.0", [null,true], [true, false], [-1, true], [-1.0, true], ["-1.0", true], [["-1.0"], false]],
|
||||||
|
["-001.0", [null,true], [true, false], [-1, true], [-1.0, true], ["-001.0", true], [["-001.0"], false]],
|
||||||
|
["-1.0e2", [null,true], [true, false], [-100, true], [-100.0, true], ["-1.0e2", true], [["-1.0e2"], false]],
|
||||||
|
["-1", [null,true], [true, false], [-1, true], [-1.0, true], ["-1", true], [["-1"], false]],
|
||||||
|
["-001", [null,true], [true, false], [-1, true], [-1.0, true], ["-001", true], [["-001"], false]],
|
||||||
|
["-1e2", [null,true], [true, false], [-100, true], [-100.0, true], ["-1e2", true], [["-1e2"], false]],
|
||||||
|
[-0, [null,true], [false,true], [0, true], [0.0, true], ["0", true], [[-0], false]],
|
||||||
|
["-0", [null,true], [false,true], [0, true], [-0.0, true], ["-0", true], [["-0"], false]],
|
||||||
|
["-000", [null,true], [false,true], [0, true], [-0.0, true], ["-000", true], [["-000"], false]],
|
||||||
|
[-0.0, [null,true], [false,true], [0, true], [-0.0, true], ["-0", true], [[-0.0], false]],
|
||||||
|
["-0.0", [null,true], [false,true], [0, true], [-0.0, true], ["-0.0", true], [["-0.0"], false]],
|
||||||
|
["-000.000", [null,true], [false,true], [0, true], [-0.0, true], ["-000.000", true], [["-000.000"], false]],
|
||||||
|
[false, [null,true], [false,true], [0, false], [0.0, false], ["", false], [[false], false]],
|
||||||
|
[true, [null,true], [true, true], [1, false], [1.0, false], ["1", false], [[true], false]],
|
||||||
|
["on", [null,true], [true, true], [0, false], [0.0, false], ["on", true], [["on"], false]],
|
||||||
|
["off", [null,true], [false,true], [0, false], [0.0, false], ["off", true], [["off"], false]],
|
||||||
|
["yes", [null,true], [true, true], [0, false], [0.0, false], ["yes", true], [["yes"], false]],
|
||||||
|
["no", [null,true], [false,true], [0, false], [0.0, false], ["no", true], [["no"], false]],
|
||||||
|
["true", [null,true], [true, true], [0, false], [0.0, false], ["true", true], [["true"], false]],
|
||||||
|
["false", [null,true], [false,true], [0, false], [0.0, false], ["false", true], [["false"], false]],
|
||||||
|
[INF, [null,true], [true, false], [0, false], [INF, true], ["INF", false], [[INF], false]],
|
||||||
|
[-INF, [null,true], [true, false], [0, false], [-INF, true], ["-INF", false], [[-INF], false]],
|
||||||
|
[NAN, [null,true], [false,false], [0, false], [NAN, true], ["NAN", false], [[], false]],
|
||||||
|
[[], [null,true], [false,false], [0, false], [0.0, false], ["", false], [[], true] ],
|
||||||
|
["some string", [null,true], [true, false], [0, false], [0.0, false], ["some string", true], [["some string"], false]],
|
||||||
|
[" ", [null,true], [true, false], [0, false], [0.0, false], [" ", true], [[" "], false]],
|
||||||
|
[new \StdClass, [null,true], [true, false], [0, false], [0.0, false], ["", false], [[new \StdClass], false]],
|
||||||
|
[new StrClass(""), [null,true], [false,true], [0, false], [0.0, false], ["", true], [[new StrClass("")], false]],
|
||||||
|
[new StrClass("1"), [null,true], [true, true], [1, true], [1.0, true], ["1", true], [[new StrClass("1")], false]],
|
||||||
|
[new StrClass("0"), [null,true], [false,true], [0, true], [0.0, true], ["0", true], [[new StrClass("0")], false]],
|
||||||
|
[new StrClass("-1"), [null,true], [true, false], [-1, true], [-1.0, true], ["-1", true], [[new StrClass("-1")], false]],
|
||||||
|
[new StrClass("Msg"), [null,true], [true, false], [0, false], [0.0, false], ["Msg", true], [[new StrClass("Msg")], false]],
|
||||||
|
[new StrClass(" "), [null,true], [true, false], [0, false], [0.0, false], [" ", true], [[new StrClass(" ")], false]],
|
||||||
|
[2.5, [null,true], [true, false], [2, false], [2.5, true], ["2.5", true], [[2.5], false]],
|
||||||
|
[0.5, [null,true], [true, false], [0, false], [0.5, true], ["0.5", true], [[0.5], false]],
|
||||||
|
["2.5", [null,true], [true, false], [2, false], [2.5, true], ["2.5", true], [["2.5"], false]],
|
||||||
|
["0.5", [null,true], [true, false], [0, false], [0.5, true], ["0.5", true], [["0.5"], false]],
|
||||||
|
[$this->d("2010-01-01T00:00:00",0,0), [null,true], [true, false], [1262304000, false], [1262304000.0, false], ["2010-01-01T00:00:00Z",true], [[$this->d("2010-01-01T00:00:00",0,0)],false]],
|
||||||
|
[$this->d("2010-01-01T00:00:00",0,1), [null,true], [true, false], [1262304000, false], [1262304000.0, false], ["2010-01-01T00:00:00Z",true], [[$this->d("2010-01-01T00:00:00",0,1)],false]],
|
||||||
|
[$this->d("2010-01-01T00:00:00",1,0), [null,true], [true, false], [1262322000, false], [1262322000.0, false], ["2010-01-01T05:00:00Z",true], [[$this->d("2010-01-01T00:00:00",1,0)],false]],
|
||||||
|
[$this->d("2010-01-01T00:00:00",1,1), [null,true], [true, false], [1262322000, false], [1262322000.0, false], ["2010-01-01T05:00:00Z",true], [[$this->d("2010-01-01T00:00:00",1,1)],false]],
|
||||||
|
[1e14, [null,true], [true, false], [100000000000000,true], [1e14, true], ["100000000000000", true], [[1e14], false]],
|
||||||
|
[1e-6, [null,true], [true, false], [0, false], [1e-6, true], ["0.000001", true], [[1e-6], false]],
|
||||||
|
[[1,2,3], [null,true], [true, false], [0, false], [0.0, false], ["", false], [[1,2,3], true] ],
|
||||||
|
[['a'=>1,'b'=>2], [null,true], [true, false], [0, false], [0.0, false], ["", false], [['a'=>1,'b'=>2], true] ],
|
||||||
|
[new Test\Result([['a'=>1,'b'=>2]]), [null,true], [true, false], [0, false], [0.0, false], ["", false], [[['a'=>1,'b'=>2]], true] ],
|
||||||
|
];
|
||||||
|
$params = [
|
||||||
|
[I::T_MIXED, "Mixed" ],
|
||||||
|
[I::T_NULL, "Null", ],
|
||||||
|
[I::T_BOOL, "Boolean", ],
|
||||||
|
[I::T_INT, "Integer", ],
|
||||||
|
[I::T_FLOAT, "Floating point"],
|
||||||
|
[I::T_STRING, "String", ],
|
||||||
|
[I::T_ARRAY, "Array", ],
|
||||||
|
];
|
||||||
|
foreach ($params as $index => $param) {
|
||||||
|
list($type, $name) = $param;
|
||||||
|
$this->assertNull(I::normalize(null, $type | I::M_STRICT | I::M_NULL), $name." null-passthrough test failed");
|
||||||
|
foreach ($tests as $test) {
|
||||||
|
list($exp, $pass) = $index ? $test[$index] : [$test[$index], true];
|
||||||
|
$value = $test[0];
|
||||||
|
$assert = (is_float($exp) && is_nan($exp) ? "assertNan" : (is_scalar($exp) ? "assertSame" : "assertEquals"));
|
||||||
|
$this->$assert($exp, I::normalize($value, $type), $name." test failed for value: ".var_export($value, true));
|
||||||
|
if ($pass) {
|
||||||
|
$this->$assert($exp, I::normalize($value, $type | I::M_DROP), $name." drop test failed for value: ".var_export($value, true));
|
||||||
|
$this->$assert($exp, I::normalize($value, $type | I::M_STRICT), $name." error test failed for value: ".var_export($value, true));
|
||||||
|
} else {
|
||||||
|
$this->assertNull(I::normalize($value, $type | I::M_DROP), $name." drop test failed for value: ".var_export($value, true));
|
||||||
|
$exc = new ExceptionType("strictFailure", $type);
|
||||||
|
try {
|
||||||
|
$act = I::normalize($value, $type | I::M_STRICT);
|
||||||
|
} catch (ExceptionType $e) {
|
||||||
|
$act = $e;
|
||||||
|
} finally {
|
||||||
|
$this->assertEquals($exc, $act, $name." error test failed for value: ".var_export($value, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// DateTimeInterface tests
|
||||||
|
$tests = [
|
||||||
|
/* Input value microtime iso8601 iso8601m http sql date time unix float '!M j, Y (D)' *strtotime* (null) */
|
||||||
|
[null, null, null, null, null, null, null, null, null, null, null, null, ],
|
||||||
|
[$this->d("2010-01-01T00:00:00",0,0), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), ],
|
||||||
|
[$this->d("2010-01-01T00:00:00",0,1), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), ],
|
||||||
|
[$this->d("2010-01-01T00:00:00",1,0), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), ],
|
||||||
|
[$this->d("2010-01-01T00:00:00",1,1), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), $this->t(1262322000), ],
|
||||||
|
[1262304000, $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), $this->t(1262304000), ],
|
||||||
|
[1262304000.123456, $this->t(1262304000.123456), $this->t(1262304000.123456), $this->t(1262304000.123456), $this->t(1262304000.123456), $this->t(1262304000.123456), $this->t(1262304000.123456), $this->t(1262304000.123456), $this->t(1262304000.123456), $this->t(1262304000.123456), $this->t(1262304000.123456), $this->t(1262304000.123456), ],
|
||||||
|
[1262304000.42, $this->t(1262304000.42), $this->t(1262304000.42), $this->t(1262304000.42), $this->t(1262304000.42), $this->t(1262304000.42), $this->t(1262304000.42), $this->t(1262304000.42), $this->t(1262304000.42), $this->t(1262304000.42), $this->t(1262304000.42), $this->t(1262304000.42), ],
|
||||||
|
["0.12345600 1262304000", $this->t(1262304000.123456), null, null, null, null, null, null, null, null, null, null, ],
|
||||||
|
["0.42 1262304000", null, null, null, null, null, null, null, null, null, null, null, ],
|
||||||
|
["2010-01-01T00:00:00", null, $this->t(1262304000), $this->t(1262304000), null, null, null, null, null, null, null, $this->t(1262304000), ],
|
||||||
|
["2010-01-01T00:00:00Z", null, $this->t(1262304000), $this->t(1262304000), null, null, null, null, null, null, null, $this->t(1262304000), ],
|
||||||
|
["2010-01-01T00:00:00+0000", null, $this->t(1262304000), $this->t(1262304000), null, null, null, null, null, null, null, $this->t(1262304000), ],
|
||||||
|
["2010-01-01T00:00:00-0000", null, $this->t(1262304000), $this->t(1262304000), null, null, null, null, null, null, null, $this->t(1262304000), ],
|
||||||
|
["2010-01-01T00:00:00+00:00", null, $this->t(1262304000), $this->t(1262304000), null, null, null, null, null, null, null, $this->t(1262304000), ],
|
||||||
|
["2010-01-01T00:00:00-05:00", null, $this->t(1262322000), $this->t(1262322000), null, null, null, null, null, null, null, $this->t(1262322000), ],
|
||||||
|
["2010-01-01T00:00:00.123456Z", null, null, $this->t(1262304000.123456), null, null, null, null, null, null, null, $this->t(1262304000.123456), ],
|
||||||
|
["Fri, 01 Jan 2010 00:00:00 GMT", null, null, null, $this->t(1262304000), null, null, null, null, null, null, $this->t(1262304000), ],
|
||||||
|
["2010-01-01 00:00:00", null, null, null, null, $this->t(1262304000), null, null, null, null, null, $this->t(1262304000), ],
|
||||||
|
["2010-01-01", null, null, null, null, null, $this->t(1262304000), null, null, null, null, $this->t(1262304000), ],
|
||||||
|
["12:34:56", null, null, null, null, null, null, $this->t(45296), null, null, null, $this->t(strtotime("today")+45296), ],
|
||||||
|
["1262304000", null, null, null, null, null, null, null, $this->t(1262304000), null, null, null, ],
|
||||||
|
["1262304000.123456", null, null, null, null, null, null, null, null, $this->t(1262304000.123456), null, null, ],
|
||||||
|
["1262304000.42", null, null, null, null, null, null, null, null, $this->t(1262304000.42), null, null, ],
|
||||||
|
["Jan 1, 2010 (Fri)", null, null, null, null, null, null, null, null, null, $this->t(1262304000), null, ],
|
||||||
|
["First day of Jan 2010 12AM", null, null, null, null, null, null, null, null, null, null, $this->t(1262304000), ],
|
||||||
|
];
|
||||||
|
$formats = [
|
||||||
|
"microtime",
|
||||||
|
"iso8601",
|
||||||
|
"iso8601m",
|
||||||
|
"http",
|
||||||
|
"sql",
|
||||||
|
"date",
|
||||||
|
"time",
|
||||||
|
"unix",
|
||||||
|
"float",
|
||||||
|
"!M j, Y (D)",
|
||||||
|
null,
|
||||||
|
];
|
||||||
|
$exc = new ExceptionType("strictFailure", I::T_DATE);
|
||||||
|
foreach ($formats as $index => $format) {
|
||||||
|
foreach ($tests as $test) {
|
||||||
|
$value = $test[0];
|
||||||
|
$exp = $test[$index+1];
|
||||||
|
$this->assertEquals($exp, I::normalize($value, I::T_DATE, $format), "Test failed for format ".var_export($format, true)." using value ".var_export($value, true));
|
||||||
|
$this->assertEquals($exp, I::normalize($value, I::T_DATE | I::M_DROP, $format), "Drop test failed for format ".var_export($format, true)." using value ".var_export($value, true));
|
||||||
|
// test for exception in case of errors
|
||||||
|
$exp = $exp ?? $exc;
|
||||||
|
try {
|
||||||
|
$act = I::normalize($value, I::T_DATE | I::M_STRICT, $format);
|
||||||
|
} catch (ExceptionType $e) {
|
||||||
|
$act = $e;
|
||||||
|
} finally {
|
||||||
|
$this->assertEquals($exp, $act, "Error test failed for format ".var_export($format, true)." using value ".var_export($value, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Array-mode tests
|
||||||
|
$tests = [
|
||||||
|
[I::T_INT | I::M_DROP, new Test\Result([1, 2, 2.2, 3]), [1,2,null,3] ],
|
||||||
|
[I::T_INT, new Test\Result([1, 2, 2.2, 3]), [1,2,2,3] ],
|
||||||
|
[I::T_STRING | I::M_STRICT, "Bare string", ["Bare string"]],
|
||||||
|
];
|
||||||
|
foreach ($tests as $index => $test) {
|
||||||
|
list($type, $value, $exp) = $test;
|
||||||
|
$this->assertEquals($exp, I::normalize($value, $type | I::M_ARRAY, "iso8601"), "Failed test #$index");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function d($spec, $local, $immutable): \DateTimeInterface {
|
||||||
|
$tz = $local ? new \DateTimeZone("America/Toronto") : new \DateTimeZone("UTC");
|
||||||
|
if ($immutable) {
|
||||||
|
return \DateTimeImmutable::createFromFormat("!Y-m-d\TH:i:s", $spec, $tz);
|
||||||
|
} else {
|
||||||
|
return \DateTime::createFromFormat("!Y-m-d\TH:i:s", $spec, $tz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function t(float $spec): \DateTime {
|
||||||
|
return \DateTime::createFromFormat("U.u", sprintf("%F", $spec), new \DateTimeZone("UTC"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -388,6 +388,7 @@ class TestNCNV1_2 extends Test\AbstractTest {
|
||||||
['id' => 2, 'name' => "Hardware", 'parent' => null],
|
['id' => 2, 'name' => "Hardware", 'parent' => null],
|
||||||
];
|
];
|
||||||
// set of various mocks for testing
|
// set of various mocks for testing
|
||||||
|
Phake::when(Arsse::$db)->folderAdd($this->anything(), $this->anything())->thenThrow(new \Exception);
|
||||||
Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, $in[0])->thenReturn(1)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call
|
Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, $in[0])->thenReturn(1)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call
|
||||||
Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, $in[1])->thenReturn(2)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call
|
Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, $in[1])->thenReturn(2)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call
|
||||||
Phake::when(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 1)->thenReturn($out[0]);
|
Phake::when(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 1)->thenReturn($out[0]);
|
||||||
|
@ -499,6 +500,7 @@ class TestNCNV1_2 extends Test\AbstractTest {
|
||||||
// set up the necessary mocks
|
// set up the necessary mocks
|
||||||
Phake::when(Arsse::$db)->subscriptionAdd(Arsse::$user->id, "http://example.com/news.atom")->thenReturn(2112)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call
|
Phake::when(Arsse::$db)->subscriptionAdd(Arsse::$user->id, "http://example.com/news.atom")->thenReturn(2112)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call
|
||||||
Phake::when(Arsse::$db)->subscriptionAdd(Arsse::$user->id, "http://example.org/news.atom")->thenReturn(42)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call
|
Phake::when(Arsse::$db)->subscriptionAdd(Arsse::$user->id, "http://example.org/news.atom")->thenReturn(42)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call
|
||||||
|
Phake::when(Arsse::$db)->subscriptionAdd(Arsse::$user->id, "")->thenThrow(new \JKingWeb\Arsse\Feed\Exception("", new \PicoFeed\Reader\SubscriptionNotFoundException));
|
||||||
Phake::when(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 2112)->thenReturn($this->feeds['db'][0]);
|
Phake::when(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 2112)->thenReturn($this->feeds['db'][0]);
|
||||||
Phake::when(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 42)->thenReturn($this->feeds['db'][1]);
|
Phake::when(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 42)->thenReturn($this->feeds['db'][1]);
|
||||||
Phake::when(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 47)->thenReturn($this->feeds['db'][2]);
|
Phake::when(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 47)->thenReturn($this->feeds['db'][2]);
|
||||||
|
@ -584,7 +586,7 @@ class TestNCNV1_2 extends Test\AbstractTest {
|
||||||
Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, $this->identicalTo(['title' => ""]))->thenThrow(new ExceptionInput("missing"));
|
Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, $this->identicalTo(['title' => ""]))->thenThrow(new ExceptionInput("missing"));
|
||||||
Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, $this->identicalTo(['title' => false]))->thenThrow(new ExceptionInput("missing"));
|
Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, $this->identicalTo(['title' => false]))->thenThrow(new ExceptionInput("missing"));
|
||||||
Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 42, $this->anything())->thenThrow(new ExceptionInput("subjectMissing"));
|
Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 42, $this->anything())->thenThrow(new ExceptionInput("subjectMissing"));
|
||||||
$exp = new Response(204);
|
$exp = new Response(422);
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[0]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[0]), 'application/json')));
|
||||||
$exp = new Response(204);
|
$exp = new Response(204);
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[1]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[1]), 'application/json')));
|
||||||
|
@ -628,7 +630,7 @@ class TestNCNV1_2 extends Test\AbstractTest {
|
||||||
];
|
];
|
||||||
Phake::when(Arsse::$db)->feedUpdate(42)->thenReturn(true);
|
Phake::when(Arsse::$db)->feedUpdate(42)->thenReturn(true);
|
||||||
Phake::when(Arsse::$db)->feedUpdate(2112)->thenThrow(new ExceptionInput("subjectMissing"));
|
Phake::when(Arsse::$db)->feedUpdate(2112)->thenThrow(new ExceptionInput("subjectMissing"));
|
||||||
Phake::when(Arsse::$db)->feedUpdate(-1)->thenThrow(new ExceptionInput("typeViolation"));
|
Phake::when(Arsse::$db)->feedUpdate($this->lessThan(1))->thenThrow(new ExceptionInput("typeViolation"));
|
||||||
$exp = new Response(204);
|
$exp = new Response(204);
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json')));
|
||||||
$exp = new Response(404);
|
$exp = new Response(404);
|
||||||
|
@ -788,7 +790,7 @@ class TestNCNV1_2 extends Test\AbstractTest {
|
||||||
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->editions($in[1]))->thenThrow(new ExceptionInput("tooLong")); // data model function limited to 50 items for multiples
|
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->editions($in[1]))->thenThrow(new ExceptionInput("tooLong")); // data model function limited to 50 items for multiples
|
||||||
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->articles([]))->thenThrow(new ExceptionInput("tooShort")); // data model function requires one valid integer for multiples
|
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->articles([]))->thenThrow(new ExceptionInput("tooShort")); // data model function requires one valid integer for multiples
|
||||||
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->articles($in[1]))->thenThrow(new ExceptionInput("tooLong")); // data model function limited to 50 items for multiples
|
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->articles($in[1]))->thenThrow(new ExceptionInput("tooLong")); // data model function limited to 50 items for multiples
|
||||||
$exp = new Response(422);
|
$exp = new Response(204);
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple")));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple")));
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple")));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple")));
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple")));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple")));
|
||||||
|
@ -797,14 +799,12 @@ class TestNCNV1_2 extends Test\AbstractTest {
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => "ook"]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => "ook"]), 'application/json')));
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => "ook"]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => "ook"]), 'application/json')));
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple", json_encode(['items' => "ook"]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple", json_encode(['items' => "ook"]), 'application/json')));
|
||||||
$exp = new Response(204);
|
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple", json_encode(['items' => []]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple", json_encode(['items' => []]), 'application/json')));
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => []]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => []]), 'application/json')));
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple", json_encode(['items' => $in[0]]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple", json_encode(['items' => $in[0]]), 'application/json')));
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => $in[0]]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => $in[0]]), 'application/json')));
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple", json_encode(['items' => $in[1]]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple", json_encode(['items' => $in[1]]), 'application/json')));
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => $in[1]]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => $in[1]]), 'application/json')));
|
||||||
$exp = new Response(204);
|
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => []]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => []]), 'application/json')));
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple", json_encode(['items' => []]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple", json_encode(['items' => []]), 'application/json')));
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => $inStar[0]]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => $inStar[0]]), 'application/json')));
|
||||||
|
@ -812,13 +812,13 @@ class TestNCNV1_2 extends Test\AbstractTest {
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => $inStar[1]]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => $inStar[1]]), 'application/json')));
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple", json_encode(['items' => $inStar[1]]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple", json_encode(['items' => $inStar[1]]), 'application/json')));
|
||||||
// ensure the data model was queried appropriately for read/unread
|
// ensure the data model was queried appropriately for read/unread
|
||||||
Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->editions([]));
|
Phake::verify(Arsse::$db, Phake::times(2))->articleMark(Arsse::$user->id, $read, (new Context)->editions([]));
|
||||||
Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->editions($in[0]));
|
Phake::verify(Arsse::$db, Phake::times(2))->articleMark(Arsse::$user->id, $read, (new Context)->editions($in[0]));
|
||||||
Phake::verify(Arsse::$db, Phake::times(0))->articleMark(Arsse::$user->id, $read, (new Context)->editions($in[1]));
|
Phake::verify(Arsse::$db, Phake::times(0))->articleMark(Arsse::$user->id, $read, (new Context)->editions($in[1]));
|
||||||
Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->editions($in[2]));
|
Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->editions($in[2]));
|
||||||
Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->editions($in[3]));
|
Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->editions($in[3]));
|
||||||
Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $unread, (new Context)->editions([]));
|
Phake::verify(Arsse::$db, Phake::times(2))->articleMark(Arsse::$user->id, $unread, (new Context)->editions([]));
|
||||||
Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $unread, (new Context)->editions($in[0]));
|
Phake::verify(Arsse::$db, Phake::times(2))->articleMark(Arsse::$user->id, $unread, (new Context)->editions($in[0]));
|
||||||
Phake::verify(Arsse::$db, Phake::times(0))->articleMark(Arsse::$user->id, $unread, (new Context)->editions($in[1]));
|
Phake::verify(Arsse::$db, Phake::times(0))->articleMark(Arsse::$user->id, $unread, (new Context)->editions($in[1]));
|
||||||
Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $unread, (new Context)->editions($in[2]));
|
Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $unread, (new Context)->editions($in[2]));
|
||||||
Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $unread, (new Context)->editions($in[3]));
|
Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $unread, (new Context)->editions($in[3]));
|
||||||
|
|
Loading…
Reference in a new issue