Allow simpler feed exception creation
This commit is contained in:
parent
6936f365e4
commit
4972c79e32
8 changed files with 60 additions and 42 deletions
10
lib/Feed.php
10
lib/Feed.php
|
@ -39,7 +39,7 @@ class Feed {
|
||||||
if (!$links) {
|
if (!$links) {
|
||||||
// work around a PicoFeed memory leak
|
// work around a PicoFeed memory leak
|
||||||
libxml_use_internal_errors(false);
|
libxml_use_internal_errors(false);
|
||||||
throw new Feed\Exception($url, new \PicoFeed\Reader\SubscriptionNotFoundException('Unable to find a subscription'));
|
throw new Feed\Exception("", ['url' => $url], new \PicoFeed\Reader\SubscriptionNotFoundException('Unable to find a subscription'));
|
||||||
} else {
|
} else {
|
||||||
$out = $links[0];
|
$out = $links[0];
|
||||||
}
|
}
|
||||||
|
@ -119,9 +119,9 @@ class Feed {
|
||||||
$client->reader = $reader;
|
$client->reader = $reader;
|
||||||
return $client;
|
return $client;
|
||||||
} catch (PicoFeedException $e) {
|
} catch (PicoFeedException $e) {
|
||||||
throw new Feed\Exception($url, $e); // @codeCoverageIgnore
|
throw new Feed\Exception("", ['url' => $url], $e); // @codeCoverageIgnore
|
||||||
} catch (\GuzzleHttp\Exception\GuzzleException $e) {
|
} catch (\GuzzleHttp\Exception\GuzzleException $e) {
|
||||||
throw new Feed\Exception($url, $e);
|
throw new Feed\Exception("", ['url' => $url], $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,9 +133,9 @@ class Feed {
|
||||||
$this->resource->getEncoding()
|
$this->resource->getEncoding()
|
||||||
)->execute();
|
)->execute();
|
||||||
} catch (PicoFeedException $e) {
|
} catch (PicoFeedException $e) {
|
||||||
throw new Feed\Exception($this->resource->getUrl(), $e);
|
throw new Feed\Exception("", ['url' => $this->resource->getUrl()], $e);
|
||||||
} catch (\GuzzleHttp\Exception\GuzzleException $e) { // @codeCoverageIgnore
|
} catch (\GuzzleHttp\Exception\GuzzleException $e) { // @codeCoverageIgnore
|
||||||
throw new Feed\Exception($this->resource->getUrl(), $e); // @codeCoverageIgnore
|
throw new Feed\Exception("", ['url' => $this->resource->getUrl()], $e); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab the favicon for the feed, or null if no valid icon is found
|
// Grab the favicon for the feed, or null if no valid icon is found
|
||||||
|
|
|
@ -15,30 +15,33 @@ class Exception extends \JKingWeb\Arsse\AbstractException {
|
||||||
protected const CURL_ERROR_MAP = [1 => "invalidUrl",3 => "invalidUrl",5 => "transmissionError","connectionFailed","connectionFailed","transmissionError","forbidden","unauthorized","transmissionError","transmissionError","transmissionError","transmissionError","connectionFailed","connectionFailed","transmissionError","transmissionError","transmissionError","transmissionError","transmissionError","invalidUrl","transmissionError","transmissionError","transmissionError","transmissionError",28 => "timeout","transmissionError","transmissionError","transmissionError","transmissionError","transmissionError",35 => "invalidCertificate","transmissionError","transmissionError","transmissionError","transmissionError",45 => "transmissionError","unauthorized","maxRedirect",52 => "transmissionError","invalidCertificate","invalidCertificate","transmissionError","transmissionError",58 => "invalidCertificate","invalidCertificate","invalidCertificate","transmissionError","invalidUrl","transmissionError","invalidCertificate","transmissionError","invalidCertificate","forbidden","invalidUrl","forbidden","transmissionError",73 => "transmissionError","transmissionError",77 => "invalidCertificate","invalidUrl",90 => "invalidCertificate","invalidCertificate","transmissionError",94 => "unauthorized","transmissionError","connectionFailed"];
|
protected const CURL_ERROR_MAP = [1 => "invalidUrl",3 => "invalidUrl",5 => "transmissionError","connectionFailed","connectionFailed","transmissionError","forbidden","unauthorized","transmissionError","transmissionError","transmissionError","transmissionError","connectionFailed","connectionFailed","transmissionError","transmissionError","transmissionError","transmissionError","transmissionError","invalidUrl","transmissionError","transmissionError","transmissionError","transmissionError",28 => "timeout","transmissionError","transmissionError","transmissionError","transmissionError","transmissionError",35 => "invalidCertificate","transmissionError","transmissionError","transmissionError","transmissionError",45 => "transmissionError","unauthorized","maxRedirect",52 => "transmissionError","invalidCertificate","invalidCertificate","transmissionError","transmissionError",58 => "invalidCertificate","invalidCertificate","invalidCertificate","transmissionError","invalidUrl","transmissionError","invalidCertificate","transmissionError","invalidCertificate","forbidden","invalidUrl","forbidden","transmissionError",73 => "transmissionError","transmissionError",77 => "invalidCertificate","invalidUrl",90 => "invalidCertificate","invalidCertificate","transmissionError",94 => "unauthorized","transmissionError","connectionFailed"];
|
||||||
protected const HTTP_ERROR_MAP = [401 => "unauthorized",403 => "forbidden",404 => "invalidUrl",408 => "timeout",410 => "invalidUrl",414 => "invalidUrl",451 => "invalidUrl"];
|
protected const HTTP_ERROR_MAP = [401 => "unauthorized",403 => "forbidden",404 => "invalidUrl",408 => "timeout",410 => "invalidUrl",414 => "invalidUrl",451 => "invalidUrl"];
|
||||||
|
|
||||||
public function __construct($url, \Throwable $e) {
|
public function __construct(string $msgID = "", $vars, \Throwable $e) {
|
||||||
if ($e instanceof BadResponseException) {
|
if ($msgID === "") {
|
||||||
$msgID = self::HTTP_ERROR_MAP[$e->getCode()] ?? "transmissionError";
|
assert($e !== null, new \Exception("Expecting Picofeed or Guzzle exception when no message specified."));
|
||||||
} elseif ($e instanceof TooManyRedirectsException) {
|
if ($e instanceof BadResponseException) {
|
||||||
$msgID = "maxRedirect";
|
$msgID = self::HTTP_ERROR_MAP[$e->getCode()] ?? "transmissionError";
|
||||||
} elseif ($e instanceof GuzzleException) {
|
} elseif ($e instanceof TooManyRedirectsException) {
|
||||||
$msg = $e->getMessage();
|
$msgID = "maxRedirect";
|
||||||
if (preg_match("/^Error creating resource:/", $msg)) {
|
} elseif ($e instanceof GuzzleException) {
|
||||||
// PHP stream error; the class of error is ambiguous
|
$msg = $e->getMessage();
|
||||||
$msgID = "transmissionError";
|
if (preg_match("/^Error creating resource:/", $msg)) {
|
||||||
} elseif (preg_match("/^cURL error (\d+):/", $msg, $match)) {
|
// PHP stream error; the class of error is ambiguous
|
||||||
$msgID = self::CURL_ERROR_MAP[(int) $match[1]] ?? "internalError";
|
$msgID = "transmissionError";
|
||||||
|
} elseif (preg_match("/^cURL error (\d+):/", $msg, $match)) {
|
||||||
|
$msgID = self::CURL_ERROR_MAP[(int) $match[1]] ?? "internalError";
|
||||||
|
} else {
|
||||||
|
$msgID = "internalError";
|
||||||
|
}
|
||||||
|
} elseif ($e instanceof PicoFeedException) {
|
||||||
|
$className = get_class($e);
|
||||||
|
// Convert the exception thrown by PicoFeed to the one to be thrown here.
|
||||||
|
$msgID = preg_replace('/^PicoFeed\\\(?:Client|Parser|Reader)\\\([A-Za-z]+)Exception$/', '$1', $className);
|
||||||
|
// If the message ID doesn't change then it's unknown.
|
||||||
|
$msgID = ($msgID !== $className) ? lcfirst($msgID) : "internalError";
|
||||||
} else {
|
} else {
|
||||||
$msgID = "internalError";
|
$msgID = "internalError";
|
||||||
}
|
}
|
||||||
} elseif ($e instanceof PicoFeedException) {
|
|
||||||
$className = get_class($e);
|
|
||||||
// Convert the exception thrown by PicoFeed to the one to be thrown here.
|
|
||||||
$msgID = preg_replace('/^PicoFeed\\\(?:Client|Parser|Reader)\\\([A-Za-z]+)Exception$/', '$1', $className);
|
|
||||||
// If the message ID doesn't change then it's unknown.
|
|
||||||
$msgID = ($msgID !== $className) ? lcfirst($msgID) : "internalError";
|
|
||||||
} else {
|
|
||||||
$msgID = "internalError";
|
|
||||||
}
|
}
|
||||||
parent::__construct($msgID, ['url' => $url], $e);
|
parent::__construct($msgID, $vars, $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
public function testRefreshAFeed(string $cmd, int $exitStatus, string $output): void {
|
public function testRefreshAFeed(string $cmd, int $exitStatus, string $output): void {
|
||||||
Arsse::$db = \Phake::mock(Database::class);
|
Arsse::$db = \Phake::mock(Database::class);
|
||||||
\Phake::when(Arsse::$db)->feedUpdate(1, true)->thenReturn(true);
|
\Phake::when(Arsse::$db)->feedUpdate(1, true)->thenReturn(true);
|
||||||
\Phake::when(Arsse::$db)->feedUpdate(2, true)->thenThrow(new \JKingWeb\Arsse\Feed\Exception("http://example.com/", $this->mockGuzzleException(ClientException::class, "", 404)));
|
\Phake::when(Arsse::$db)->feedUpdate(2, true)->thenThrow(new \JKingWeb\Arsse\Feed\Exception("", ['url' => "http://example.com/"], $this->mockGuzzleException(ClientException::class, "", 404)));
|
||||||
$this->assertConsole($this->cli, $cmd, $exitStatus, $output);
|
$this->assertConsole($this->cli, $cmd, $exitStatus, $output);
|
||||||
\Phake::verify($this->cli)->loadConf;
|
\Phake::verify($this->cli)->loadConf;
|
||||||
\Phake::verify(Arsse::$db)->feedUpdate;
|
\Phake::verify(Arsse::$db)->feedUpdate;
|
||||||
|
|
|
@ -251,7 +251,7 @@ trait SeriesSubscription {
|
||||||
public function testAddASubscriptionToAnInvalidFeed(): void {
|
public function testAddASubscriptionToAnInvalidFeed(): void {
|
||||||
$url = "http://example.org/feed1";
|
$url = "http://example.org/feed1";
|
||||||
$feedID = $this->nextID("arsse_feeds");
|
$feedID = $this->nextID("arsse_feeds");
|
||||||
\Phake::when(Arsse::$db)->feedUpdate->thenThrow(new FeedException($url, $this->mockGuzzleException(ClientException::class, "", 404)));
|
\Phake::when(Arsse::$db)->feedUpdate->thenThrow(new FeedException("", ['url' => $url], $this->mockGuzzleException(ClientException::class, "", 404)));
|
||||||
$this->assertException("invalidUrl", "Feed");
|
$this->assertException("invalidUrl", "Feed");
|
||||||
try {
|
try {
|
||||||
Arsse::$db->subscriptionAdd($this->user, $url, "", "", false);
|
Arsse::$db->subscriptionAdd($this->user, $url, "", "", false);
|
||||||
|
|
|
@ -20,7 +20,7 @@ class TestException extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
public function testHandleCurlErrors(int $code, string $message): void {
|
public function testHandleCurlErrors(int $code, string $message): void {
|
||||||
$e = $this->mockGuzzleException(TransferException::class, "cURL error $code: Some message", 0);
|
$e = $this->mockGuzzleException(TransferException::class, "cURL error $code: Some message", 0);
|
||||||
$this->assertException($message, "Feed");
|
$this->assertException($message, "Feed");
|
||||||
throw new FeedException("https://example.com/", $e);
|
throw new FeedException("", ['url' => "https://example.com/"], $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideCurlErrors() {
|
public function provideCurlErrors() {
|
||||||
|
@ -119,7 +119,7 @@ class TestException extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
public function testHandleHttpErrors(int $code, string $message): void {
|
public function testHandleHttpErrors(int $code, string $message): void {
|
||||||
$e = $this->mockGuzzleException(BadResponseException::class, "Irrelevant message", $code);
|
$e = $this->mockGuzzleException(BadResponseException::class, "Irrelevant message", $code);
|
||||||
$this->assertException($message, "Feed");
|
$this->assertException($message, "Feed");
|
||||||
throw new FeedException("https://example.com/", $e);
|
throw new FeedException("", ['url' => "https://example.com/"], $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideHTTPErrors() {
|
public function provideHTTPErrors() {
|
||||||
|
@ -145,7 +145,7 @@ class TestException extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
/** @dataProvider providePicoFeedException */
|
/** @dataProvider providePicoFeedException */
|
||||||
public function testHandlePicofeedException(PicoFeedException $e, string $message) {
|
public function testHandlePicofeedException(PicoFeedException $e, string $message) {
|
||||||
$this->assertException($message, "Feed");
|
$this->assertException($message, "Feed");
|
||||||
throw new FeedException("https://example.com/", $e);
|
throw new FeedException("", ['url' => "https://example.com/"], $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function providePicoFeedException() {
|
public function providePicoFeedException() {
|
||||||
|
@ -160,18 +160,18 @@ class TestException extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
public function testHandleExcessRedirections() {
|
public function testHandleExcessRedirections() {
|
||||||
$e = $this->mockGuzzleException(TooManyRedirectsException::class, "Irrelevant message", 404);
|
$e = $this->mockGuzzleException(TooManyRedirectsException::class, "Irrelevant message", 404);
|
||||||
$this->assertException("maxRedirect", "Feed");
|
$this->assertException("maxRedirect", "Feed");
|
||||||
throw new FeedException("https://example.com/", $e);
|
throw new FeedException("", ['url' => "https://example.com/"], $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testHandleGenericStreamErrors() {
|
public function testHandleGenericStreamErrors() {
|
||||||
$e = $this->mockGuzzleException(TransferException::class, "Error creating resource: Irrelevant message", 403);
|
$e = $this->mockGuzzleException(TransferException::class, "Error creating resource: Irrelevant message", 403);
|
||||||
$this->assertException("transmissionError", "Feed");
|
$this->assertException("transmissionError", "Feed");
|
||||||
throw new FeedException("https://example.com/", $e);
|
throw new FeedException("", ['url' => "https://example.com/"], $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testHandleUnexpectedError() {
|
public function testHandleUnexpectedError() {
|
||||||
$e = new \Exception;
|
$e = new \Exception;
|
||||||
$this->assertException("internalError", "Feed");
|
$this->assertException("internalError", "Feed");
|
||||||
throw new FeedException("https://example.com/", $e);
|
throw new FeedException("", ['url' => "https://example.com/"], $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -534,7 +534,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideNewSubscriptions(): array {
|
public function provideNewSubscriptions(): array {
|
||||||
$feedException = new \JKingWeb\Arsse\Feed\Exception("", new \PicoFeed\Reader\SubscriptionNotFoundException);
|
$feedException = new \JKingWeb\Arsse\Feed\Exception("", [], new \PicoFeed\Reader\SubscriptionNotFoundException);
|
||||||
return [
|
return [
|
||||||
[['url' => "http://example.com/news.atom", 'folderId' => 3], 2112, 0, $this->feeds['db'][0], new ExceptionInput("idMissing"), new Response(['feeds' => [$this->feeds['rest'][0]]])],
|
[['url' => "http://example.com/news.atom", 'folderId' => 3], 2112, 0, $this->feeds['db'][0], new ExceptionInput("idMissing"), new Response(['feeds' => [$this->feeds['rest'][0]]])],
|
||||||
[['url' => "http://example.org/news.atom", 'folderId' => 8], 42, 4758915, $this->feeds['db'][1], true, new Response(['feeds' => [$this->feeds['rest'][1]], 'newestItemId' => 4758915])],
|
[['url' => "http://example.org/news.atom", 'folderId' => 8], 42, 4758915, $this->feeds['db'][1], true, new Response(['feeds' => [$this->feeds['rest'][1]], 'newestItemId' => 4758915])],
|
||||||
|
|
|
@ -787,13 +787,13 @@ LONG_STRING;
|
||||||
];
|
];
|
||||||
$out = [
|
$out = [
|
||||||
['code' => 1, 'feed_id' => 2],
|
['code' => 1, 'feed_id' => 2],
|
||||||
['code' => 5, 'message' => (new \JKingWeb\Arsse\Feed\Exception("http://example.com/1", $this->mockGuzzleException(ClientException::class, "", 401)))->getMessage()],
|
['code' => 5, 'message' => (new \JKingWeb\Arsse\Feed\Exception("", ['url' => "http://example.com/1"], $this->mockGuzzleException(ClientException::class, "", 401)))->getMessage()],
|
||||||
['code' => 1, 'feed_id' => 0],
|
['code' => 1, 'feed_id' => 0],
|
||||||
['code' => 0, 'feed_id' => 3],
|
['code' => 0, 'feed_id' => 3],
|
||||||
['code' => 0, 'feed_id' => 1],
|
['code' => 0, 'feed_id' => 1],
|
||||||
['code' => 3, 'message' => (new \JKingWeb\Arsse\Feed\Exception("http://localhost:8000/Feed/Discovery/Invalid", new \PicoFeed\Reader\SubscriptionNotFoundException()))->getMessage()],
|
['code' => 3, 'message' => (new \JKingWeb\Arsse\Feed\Exception("", ['url' => "http://localhost:8000/Feed/Discovery/Invalid"], new \PicoFeed\Reader\SubscriptionNotFoundException()))->getMessage()],
|
||||||
['code' => 2, 'message' => (new \JKingWeb\Arsse\Feed\Exception("http://example.com/6", $this->mockGuzzleException(ClientException::class, "", 404)))->getMessage()],
|
['code' => 2, 'message' => (new \JKingWeb\Arsse\Feed\Exception("", ['url' => "http://example.com/6"], $this->mockGuzzleException(ClientException::class, "", 404)))->getMessage()],
|
||||||
['code' => 6, 'message' => (new \JKingWeb\Arsse\Feed\Exception("http://example.com/7", new \PicoFeed\Parser\MalformedXmlException()))->getMessage()],
|
['code' => 6, 'message' => (new \JKingWeb\Arsse\Feed\Exception("", ['url' => "http://example.com/7"], new \PicoFeed\Parser\MalformedXmlException()))->getMessage()],
|
||||||
['code' => 1, 'feed_id' => 4],
|
['code' => 1, 'feed_id' => 4],
|
||||||
['code' => 0, 'feed_id' => 4],
|
['code' => 0, 'feed_id' => 4],
|
||||||
];
|
];
|
||||||
|
@ -804,13 +804,13 @@ LONG_STRING;
|
||||||
['id' => 4, 'url' => "http://example.com/9"],
|
['id' => 4, 'url' => "http://example.com/9"],
|
||||||
];
|
];
|
||||||
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[0])->thenReturn(2);
|
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[0])->thenReturn(2);
|
||||||
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[1])->thenThrow(new \JKingWeb\Arsse\Feed\Exception("http://example.com/1", $this->mockGuzzleException(ClientException::class, "", 401)));
|
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[1])->thenThrow(new \JKingWeb\Arsse\Feed\Exception("", ['url' => "http://example.com/1"], $this->mockGuzzleException(ClientException::class, "", 401)));
|
||||||
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[2])->thenReturn(2);
|
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[2])->thenReturn(2);
|
||||||
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[3])->thenThrow(new ExceptionInput("constraintViolation"));
|
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[3])->thenThrow(new ExceptionInput("constraintViolation"));
|
||||||
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[4])->thenThrow(new ExceptionInput("constraintViolation"));
|
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[4])->thenThrow(new ExceptionInput("constraintViolation"));
|
||||||
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[5])->thenThrow(new ExceptionInput("constraintViolation"));
|
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[5])->thenThrow(new ExceptionInput("constraintViolation"));
|
||||||
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[6])->thenThrow(new \JKingWeb\Arsse\Feed\Exception("http://example.com/6", $this->mockGuzzleException(ClientException::class, "", 404)));
|
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[6])->thenThrow(new \JKingWeb\Arsse\Feed\Exception("", ['url' => "http://example.com/6"], $this->mockGuzzleException(ClientException::class, "", 404)));
|
||||||
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[7])->thenThrow(new \JKingWeb\Arsse\Feed\Exception("http://example.com/7", new \PicoFeed\Parser\MalformedXmlException()));
|
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[7])->thenThrow(new \JKingWeb\Arsse\Feed\Exception("", ['url' => "http://example.com/7"], new \PicoFeed\Parser\MalformedXmlException()));
|
||||||
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[8])->thenReturn(4);
|
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[8])->thenReturn(4);
|
||||||
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[9])->thenThrow(new ExceptionInput("constraintViolation"));
|
\Phake::when(Arsse::$db)->subscriptionAdd(...$db[9])->thenThrow(new ExceptionInput("constraintViolation"));
|
||||||
\Phake::when(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 42)->thenReturn($this->v(['id' => 42]));
|
\Phake::when(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 42)->thenReturn($this->v(['id' => 42]));
|
||||||
|
|
15
tests/lib/FeedException.php
Normal file
15
tests/lib/FeedException.php
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
/** @license MIT
|
||||||
|
* Copyright 2017 J. King, Dustin Wilson et al.
|
||||||
|
* See LICENSE and AUTHORS files for details */
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\Arsse\Test;
|
||||||
|
|
||||||
|
use JKingWeb\Arsse\AbstractException;
|
||||||
|
|
||||||
|
class FeedException extends \JKingWeb\Arsse\Feed\Exception {
|
||||||
|
public function __construct(string $msgID = "", $vars = null, \Throwable $e = null) {
|
||||||
|
AbstractException::__construct($msgID, $vars, $e);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue