mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Experimental forking service and accompanying CLI
- Improves #48, #57, and #61
This commit is contained in:
parent
70f76f77fa
commit
1b970cc7c5
11 changed files with 249 additions and 61 deletions
15
arsse.php
15
arsse.php
|
@ -1,10 +1,19 @@
|
||||||
<?php
|
<?php
|
||||||
namespace JKingWeb\Arsse;
|
namespace JKingWeb\Arsse;
|
||||||
|
var_export(get_defined_constants());
|
||||||
|
exit;
|
||||||
require_once __DIR__.DIRECTORY_SEPARATOR."bootstrap.php";
|
require_once __DIR__.DIRECTORY_SEPARATOR."bootstrap.php";
|
||||||
Arsse::load(new Conf());
|
|
||||||
|
|
||||||
if(\PHP_SAPI=="cli") {
|
if(\PHP_SAPI=="cli") {
|
||||||
(new Service)->watch();
|
// initialize the CLI; this automatically handles --help and --version
|
||||||
|
$cli = new CLI;
|
||||||
|
// load configuration
|
||||||
|
Arsse::load(new Conf());
|
||||||
|
// handle CLI requests
|
||||||
|
$cli->dispatch();
|
||||||
} else {
|
} else {
|
||||||
(new REST)->dispatch();
|
// load configuration
|
||||||
|
Arsse::load(new Conf());
|
||||||
|
// handle Web requests
|
||||||
|
(new REST)->dispatch()->output();
|
||||||
}
|
}
|
|
@ -25,7 +25,8 @@
|
||||||
"fguillot/picofeed": ">=0.1.31",
|
"fguillot/picofeed": ">=0.1.31",
|
||||||
"jkingweb/druuid": "^3.0.0",
|
"jkingweb/druuid": "^3.0.0",
|
||||||
"phpseclib/phpseclib": "^2.0",
|
"phpseclib/phpseclib": "^2.0",
|
||||||
"hosteurope/password-generator": "^1.0"
|
"hosteurope/password-generator": "^1.0",
|
||||||
|
"docopt/docopt": "^1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"mikey179/vfsStream": "^1.6",
|
"mikey179/vfsStream": "^1.6",
|
||||||
|
|
62
composer.lock
generated
62
composer.lock
generated
|
@ -4,8 +4,54 @@
|
||||||
"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": "f86e3cf99b80693dffb2a1e47e0b657d",
|
"content-hash": "360a767ae23dbd1b702c1b3b8b08b683",
|
||||||
"packages": [
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "docopt/docopt",
|
||||||
|
"version": "1.0.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/docopt/docopt.php.git",
|
||||||
|
"reference": "d2ee65c2fe4be78f945a48edd02be45843b39423"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/docopt/docopt.php/zipball/d2ee65c2fe4be78f945a48edd02be45843b39423",
|
||||||
|
"reference": "d2ee65c2fe4be78f945a48edd02be45843b39423",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "4.1.*"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"classmap": [
|
||||||
|
"src/docopt.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Blake Williams",
|
||||||
|
"email": "code@shabbyrobe.org",
|
||||||
|
"homepage": "http://docopt.org/",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Port of Python's docopt for PHP 5.3",
|
||||||
|
"homepage": "http://github.com/docopt/docopt.php",
|
||||||
|
"keywords": [
|
||||||
|
"cli",
|
||||||
|
"docs"
|
||||||
|
],
|
||||||
|
"time": "2015-10-30T03:21:23+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "fguillot/picofeed",
|
"name": "fguillot/picofeed",
|
||||||
"version": "v0.1.35",
|
"version": "v0.1.35",
|
||||||
|
@ -3000,7 +3046,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/config",
|
"name": "symfony/config",
|
||||||
"version": "v2.8.24",
|
"version": "v2.8.25",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/config.git",
|
"url": "https://github.com/symfony/config.git",
|
||||||
|
@ -3056,7 +3102,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/console",
|
"name": "symfony/console",
|
||||||
"version": "v2.8.24",
|
"version": "v2.8.25",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/console.git",
|
"url": "https://github.com/symfony/console.git",
|
||||||
|
@ -3174,7 +3220,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/event-dispatcher",
|
"name": "symfony/event-dispatcher",
|
||||||
"version": "v2.8.24",
|
"version": "v2.8.25",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||||
|
@ -3283,7 +3329,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/finder",
|
"name": "symfony/finder",
|
||||||
"version": "v2.8.24",
|
"version": "v2.8.25",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/finder.git",
|
"url": "https://github.com/symfony/finder.git",
|
||||||
|
@ -3391,7 +3437,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/process",
|
"name": "symfony/process",
|
||||||
"version": "v2.8.24",
|
"version": "v2.8.25",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/process.git",
|
"url": "https://github.com/symfony/process.git",
|
||||||
|
@ -3440,7 +3486,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/stopwatch",
|
"name": "symfony/stopwatch",
|
||||||
"version": "v2.8.24",
|
"version": "v2.8.25",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/stopwatch.git",
|
"url": "https://github.com/symfony/stopwatch.git",
|
||||||
|
@ -3553,7 +3599,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/validator",
|
"name": "symfony/validator",
|
||||||
"version": "v2.8.24",
|
"version": "v2.8.25",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/validator.git",
|
"url": "https://github.com/symfony/validator.git",
|
||||||
|
|
53
lib/CLI.php
Normal file
53
lib/CLI.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\Arsse;
|
||||||
|
|
||||||
|
class CLI {
|
||||||
|
protected $args = [];
|
||||||
|
|
||||||
|
protected function usage(): string {
|
||||||
|
$prog = basename($_SERVER['argv'][0]);
|
||||||
|
return <<<USAGE_TEXT
|
||||||
|
Usage:
|
||||||
|
$prog daemon
|
||||||
|
$prog feed refresh <n>
|
||||||
|
$prog --version
|
||||||
|
$prog --help | -h
|
||||||
|
|
||||||
|
The Arsse command-line interface currently allows you to start the refresh
|
||||||
|
daemon or refresh a specific feed by numeric ID.
|
||||||
|
USAGE_TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
function __construct(array $argv = null) {
|
||||||
|
if(is_null($argv)) {
|
||||||
|
$argv = array_slice($_SERVER['argv'], 1);
|
||||||
|
}
|
||||||
|
$this->args = \Docopt::handle($this->usage(), [
|
||||||
|
'argv' => $argv,
|
||||||
|
'help' => true,
|
||||||
|
'version' => VERSION,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatch(array $args = null): int {
|
||||||
|
// act on command line
|
||||||
|
if(is_null($args)) {
|
||||||
|
$args = $this->args;
|
||||||
|
}
|
||||||
|
if($args['daemon']) {
|
||||||
|
return $this->daemon();
|
||||||
|
} elseif($args['feed'] && $args['refresh']) {
|
||||||
|
return $this->feedRefresh((int) $args['<n>']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function daemon(bool $loop = true): int {
|
||||||
|
(new Service)->watch($loop);
|
||||||
|
return 0; // FIXME: should return the exception code of thrown exceptions
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function feedRefresh(int $id): int {
|
||||||
|
return (int) !Arsse::$db->feedUpdate($id);
|
||||||
|
}
|
||||||
|
}
|
17
lib/Conf.php
17
lib/Conf.php
|
@ -56,7 +56,7 @@ class Conf {
|
||||||
public $userTempPasswordLength = 20;
|
public $userTempPasswordLength = 20;
|
||||||
|
|
||||||
/** @var string Class of the background feed update service driver in use (Forking by default) */
|
/** @var string Class of the background feed update service driver in use (Forking by default) */
|
||||||
public $serviceDriver = Service\Internal\Driver::class;
|
public $serviceDriver = Service\Forking\Driver::class;
|
||||||
/** @var string The interval between checks for new feeds, as an ISO 8601 duration
|
/** @var string The interval between checks for new feeds, as an ISO 8601 duration
|
||||||
* @see https://en.wikipedia.org/wiki/ISO_8601#Durations
|
* @see https://en.wikipedia.org/wiki/ISO_8601#Durations
|
||||||
*/
|
*/
|
||||||
|
@ -84,7 +84,9 @@ class Conf {
|
||||||
* @see self::importFile()
|
* @see self::importFile()
|
||||||
*/
|
*/
|
||||||
public function __construct(string $import_file = "") {
|
public function __construct(string $import_file = "") {
|
||||||
if($import_file != "") $this->importFile($import_file);
|
if($import_file != "") {
|
||||||
|
$this->importFile($import_file);
|
||||||
|
}
|
||||||
if(is_null($this->fetchUserAgentString)) {
|
if(is_null($this->fetchUserAgentString)) {
|
||||||
$this->fetchUserAgentString = sprintf('Arsse/%s (%s %s; %s; https://code.jkingweb.ca/jking/arsse) PicoFeed (https://github.com/fguillot/picoFeed)',
|
$this->fetchUserAgentString = sprintf('Arsse/%s (%s %s; %s; https://code.jkingweb.ca/jking/arsse) PicoFeed (https://github.com/fguillot/picoFeed)',
|
||||||
VERSION, // Arsse version
|
VERSION, // Arsse version
|
||||||
|
@ -100,8 +102,11 @@ class Conf {
|
||||||
* The file must be a PHP script which return an array with keys that match the properties of the Conf class. Malformed files will throw an exception; unknown keys are silently ignored. Files may be imported is succession, though this is not currently used.
|
* The file must be a PHP script which return an array with keys that match the properties of the Conf class. Malformed files will throw an exception; unknown keys are silently ignored. Files may be imported is succession, though this is not currently used.
|
||||||
* @param string $file Full path and file name for the file to import */
|
* @param string $file Full path and file name for the file to import */
|
||||||
public function importFile(string $file): self {
|
public function importFile(string $file): self {
|
||||||
if(!file_exists($file)) throw new Conf\Exception("fileMissing", $file);
|
if(!file_exists($file)) {
|
||||||
if(!is_readable($file)) throw new Conf\Exception("fileUnreadable", $file);
|
throw new Conf\Exception("fileMissing", $file);
|
||||||
|
} else if(!is_readable($file)) {
|
||||||
|
throw new Conf\Exception("fileUnreadable", $file);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
ob_start();
|
ob_start();
|
||||||
$arr = (@include $file);
|
$arr = (@include $file);
|
||||||
|
@ -110,7 +115,9 @@ class Conf {
|
||||||
} finally {
|
} finally {
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
}
|
}
|
||||||
if(!is_array($arr)) throw new Conf\Exception("fileCorrupt", $file);
|
if(!is_array($arr)) {
|
||||||
|
throw new Conf\Exception("fileCorrupt", $file);
|
||||||
|
}
|
||||||
return $this->import($arr);
|
return $this->import($arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,31 +18,34 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
|
||||||
|
|
||||||
public function __construct(bool $install = false) {
|
public function __construct(bool $install = false) {
|
||||||
// check to make sure required extension is loaded
|
// check to make sure required extension is loaded
|
||||||
if(!class_exists("SQLite3")) throw new Exception("extMissing", self::driverName());
|
if(!class_exists("SQLite3")) {
|
||||||
|
throw new Exception("extMissing", self::driverName());
|
||||||
|
}
|
||||||
$file = Arsse::$conf->dbSQLite3File;
|
$file = Arsse::$conf->dbSQLite3File;
|
||||||
// if the file exists (or we're initializing the database), try to open it
|
// if the file exists (or we're initializing the database), try to open it
|
||||||
try {
|
try {
|
||||||
$this->db = $this->makeConnection($file, ($install) ? \SQLITE3_OPEN_READWRITE | \SQLITE3_OPEN_CREATE : \SQLITE3_OPEN_READWRITE, Arsse::$conf->dbSQLite3Key);
|
$this->db = $this->makeConnection($file, \SQLITE3_OPEN_CREATE | \SQLITE3_OPEN_READWRITE, Arsse::$conf->dbSQLite3Key);
|
||||||
} catch(\Throwable $e) {
|
|
||||||
// if opening the database doesn't work, check various pre-conditions to find out what the problem might be
|
|
||||||
if(!file_exists($file)) {
|
|
||||||
if($install && !is_writable(dirname($file))) throw new Exception("fileUncreatable", dirname($file));
|
|
||||||
throw new Exception("fileMissing", $file);
|
|
||||||
}
|
|
||||||
if(!is_readable($file) && !is_writable($file)) throw new Exception("fileUnusable", $file);
|
|
||||||
if(!is_readable($file)) throw new Exception("fileUnreadable", $file);
|
|
||||||
if(!is_writable($file)) throw new Exception("fileUnwritable", $file);
|
|
||||||
// otherwise the database is probably corrupt
|
|
||||||
throw new Exception("fileCorrupt", $mainfile);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// set initial options
|
// set initial options
|
||||||
$this->db->enableExceptions(true);
|
$this->db->enableExceptions(true);
|
||||||
$this->exec("PRAGMA journal_mode = wal");
|
$this->exec("PRAGMA journal_mode = wal");
|
||||||
$this->exec("PRAGMA foreign_keys = yes");
|
$this->exec("PRAGMA foreign_keys = yes");
|
||||||
} catch(\Exception $e) {
|
} catch(\Throwable $e) {
|
||||||
list($excClass, $excMsg, $excData) = $this->exceptionBuild();
|
// if opening the database doesn't work, check various pre-conditions to find out what the problem might be
|
||||||
throw new $excClass($excMsg, $excData);
|
if(!file_exists($file)) {
|
||||||
|
if($install && !is_writable(dirname($file))) {
|
||||||
|
throw new Exception("fileUncreatable", dirname($file));
|
||||||
|
}
|
||||||
|
throw new Exception("fileMissing", $file);
|
||||||
|
}
|
||||||
|
if(!is_readable($file) && !is_writable($file)) {
|
||||||
|
throw new Exception("fileUnusable", $file);
|
||||||
|
} else if(!is_readable($file)) {
|
||||||
|
throw new Exception("fileUnreadable", $file);
|
||||||
|
} else if(!is_writable($file)) {
|
||||||
|
throw new Exception("fileUnwritable", $file);
|
||||||
|
}
|
||||||
|
// otherwise the database is probably corrupt
|
||||||
|
throw new Exception("fileCorrupt", $mainfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,8 +69,11 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
|
||||||
|
|
||||||
public function schemaUpdate(int $to): bool {
|
public function schemaUpdate(int $to): bool {
|
||||||
$ver = $this->schemaVersion();
|
$ver = $this->schemaVersion();
|
||||||
if(!Arsse::$conf->dbAutoUpdate) throw new Exception("updateManual", ['version' => $ver, 'driver_name' => $this->driverName()]);
|
if(!Arsse::$conf->dbAutoUpdate) {
|
||||||
if($ver >= $to) throw new Exception("updateTooNew", ['difference' => ($ver - $to), 'current' => $ver, 'target' => $to, 'driver_name' => $this->driverName()]);
|
throw new Exception("updateManual", ['version' => $ver, 'driver_name' => $this->driverName()]);
|
||||||
|
} else if($ver >= $to) {
|
||||||
|
throw new Exception("updateTooNew", ['difference' => ($ver - $to), 'current' => $ver, 'target' => $to, 'driver_name' => $this->driverName()]);
|
||||||
|
}
|
||||||
$sep = \DIRECTORY_SEPARATOR;
|
$sep = \DIRECTORY_SEPARATOR;
|
||||||
$path = Arsse::$conf->dbSchemaBase.$sep."SQLite3".$sep;
|
$path = Arsse::$conf->dbSchemaBase.$sep."SQLite3".$sep;
|
||||||
// lock the database
|
// lock the database
|
||||||
|
@ -76,16 +82,23 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
|
||||||
$this->savepointCreate();
|
$this->savepointCreate();
|
||||||
try {
|
try {
|
||||||
$file = $path.$a.".sql";
|
$file = $path.$a.".sql";
|
||||||
if(!file_exists($file)) throw new Exception("updateFileMissing", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]);
|
if(!file_exists($file)) {
|
||||||
if(!is_readable($file)) throw new Exception("updateFileUnreadable", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]);
|
throw new Exception("updateFileMissing", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]);
|
||||||
|
} else if(!is_readable($file)) {
|
||||||
|
throw new Exception("updateFileUnreadable", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]);
|
||||||
|
}
|
||||||
$sql = @file_get_contents($file);
|
$sql = @file_get_contents($file);
|
||||||
if($sql===false) throw new Exception("updateFileUnusable", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]);
|
if($sql===false) {
|
||||||
|
throw new Exception("updateFileUnusable", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
$this->exec($sql);
|
$this->exec($sql);
|
||||||
} catch(\Throwable $e) {
|
} catch(\Throwable $e) {
|
||||||
throw new Exception("updateFileError", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a, 'message' => $this->getError()]);
|
throw new Exception("updateFileError", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a, 'message' => $this->getError()]);
|
||||||
}
|
}
|
||||||
if($this->schemaVersion() != $a+1) throw new Exception("updateFileIncomplete", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]);
|
if($this->schemaVersion() != $a+1) {
|
||||||
|
throw new Exception("updateFileIncomplete", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]);
|
||||||
|
}
|
||||||
} catch(\Throwable $e) {
|
} catch(\Throwable $e) {
|
||||||
// undo any partial changes from the failed update
|
// undo any partial changes from the failed update
|
||||||
$this->savepointUndo();
|
$this->savepointUndo();
|
||||||
|
|
21
lib/REST.php
21
lib/REST.php
|
@ -30,7 +30,7 @@ class REST {
|
||||||
function __construct() {
|
function __construct() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function dispatch(REST\Request $req = null): bool {
|
function dispatch(REST\Request $req = null): REST\Response {
|
||||||
if($req===null) {
|
if($req===null) {
|
||||||
$req = new REST\Request();
|
$req = new REST\Request();
|
||||||
}
|
}
|
||||||
|
@ -39,24 +39,7 @@ class REST {
|
||||||
$req->refreshURL();
|
$req->refreshURL();
|
||||||
$class = $this->apis[$api]['class'];
|
$class = $this->apis[$api]['class'];
|
||||||
$drv = new $class();
|
$drv = new $class();
|
||||||
$out = $drv->dispatch($req);
|
return $drv->dispatch($req);
|
||||||
header("Status: ".$out->code." ".Arsse::$lang->msg("HTTP.Status.".$out->code));
|
|
||||||
if(!is_null($out->payload)) {
|
|
||||||
header("Content-Type: ".$out->type);
|
|
||||||
switch($out->type) {
|
|
||||||
case REST\Response::T_JSON:
|
|
||||||
$body = json_encode($out->payload,\JSON_PRETTY_PRINT);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$body = (string) $out->payload;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach($out->fields as $field) {
|
|
||||||
header($field);
|
|
||||||
}
|
|
||||||
echo $body;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function apiMatch(string $url, array $map): string {
|
function apiMatch(string $url, array $map): string {
|
||||||
|
|
6
lib/REST/Exception.php
Normal file
6
lib/REST/Exception.php
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\Arsse\REST;
|
||||||
|
|
||||||
|
class Exception extends \JKingWeb\Arsse\AbstractException {
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
namespace JKingWeb\Arsse\REST;
|
namespace JKingWeb\Arsse\REST;
|
||||||
|
use JKingWeb\Arsse\Arsse;
|
||||||
|
|
||||||
class Response {
|
class Response {
|
||||||
const T_JSON = "application/json";
|
const T_JSON = "application/json";
|
||||||
|
@ -19,4 +20,28 @@ class Response {
|
||||||
$this->type = $type;
|
$this->type = $type;
|
||||||
$this->fields = $extraFields;
|
$this->fields = $extraFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function output() {
|
||||||
|
if(!headers_sent()) {
|
||||||
|
header("Status: ".$this->code." ".Arsse::$lang->msg("HTTP.Status.".$this->code));
|
||||||
|
$body = "";
|
||||||
|
if(!is_null($this->payload)) {
|
||||||
|
header("Content-Type: ".$this->type);
|
||||||
|
switch($this->type) {
|
||||||
|
case self::T_JSON:
|
||||||
|
$body = json_encode($this->payload,\JSON_PRETTY_PRINT);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$body = (string) $this->payload;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach($this->fields as $field) {
|
||||||
|
header($field);
|
||||||
|
}
|
||||||
|
echo $body;
|
||||||
|
} else {
|
||||||
|
throw new REST\Exception("headersSent");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
43
lib/Service/Forking/Driver.php
Normal file
43
lib/Service/Forking/Driver.php
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\Arsse\Service\Forking;
|
||||||
|
use JKingWeb\Arsse\Arsse;
|
||||||
|
|
||||||
|
class Driver implements \JKingWeb\Arsse\Service\Driver {
|
||||||
|
protected $queue = [];
|
||||||
|
|
||||||
|
static function driverName(): string {
|
||||||
|
return Arsse::$lang->msg("Driver.Service.Forking.Name");
|
||||||
|
}
|
||||||
|
|
||||||
|
static function requirementsMet(): bool {
|
||||||
|
return function_exists("popen");
|
||||||
|
}
|
||||||
|
|
||||||
|
function __construct() {
|
||||||
|
}
|
||||||
|
|
||||||
|
function queue(int ...$feeds): int {
|
||||||
|
$this->queue = array_merge($this->queue, $feeds);
|
||||||
|
return sizeof($this->queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exec(): int {
|
||||||
|
$pp = [];
|
||||||
|
while($this->queue) {
|
||||||
|
$id = (int) array_shift($this->queue);
|
||||||
|
array_push($pp, popen('"'.\PHP_BINARY.'" "'.$_SERVER['argv'][0].'" feed refresh '.$id, "r"));
|
||||||
|
}
|
||||||
|
while($pp) {
|
||||||
|
$p = array_pop($pp);
|
||||||
|
fgets($p); // TODO: log output
|
||||||
|
pclose($p);
|
||||||
|
}
|
||||||
|
return Arsse::$conf->serviceQueueWidth - sizeof($this->queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clean(): bool {
|
||||||
|
$this->queue = [];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,9 @@
|
||||||
<directory suffix=".php">../lib</directory>
|
<directory suffix=".php">../lib</directory>
|
||||||
</whitelist>
|
</whitelist>
|
||||||
</filter>
|
</filter>
|
||||||
|
<logging>
|
||||||
|
<log type="coverage-html" target="coverage" showUncoveredFiles="true"/>
|
||||||
|
</logging>
|
||||||
|
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="Exceptions">
|
<testsuite name="Exceptions">
|
||||||
|
@ -62,6 +65,5 @@
|
||||||
<testsuite name="Refresh service">
|
<testsuite name="Refresh service">
|
||||||
<file>Service/TestService.php</file>
|
<file>Service/TestService.php</file>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
|
|
||||||
</testsuites>
|
</testsuites>
|
||||||
</phpunit>
|
</phpunit>
|
Loading…
Reference in a new issue