Wrong parameters error in extended Exception class in PHP 7.0
Wrong parameters error in extended Exception class in PHP 7.0
我正在 PHP 7.0 (Ubuntu 16.04) 上使用 Slim Framework 构建 REST API。为了正确处理异常,我扩展了基础 \Exception
class 如下:
namespace App\Exceptions;
class AppException extends \Exception
{
}
然后我将其用作所有应用程序异常的基本异常。对于所有向用户提供 JSON 响应的异常,我写了另一个 class :
namespace App\Exceptions;
use Slim\Container;
use Slim\Http\Request;
use Slim\Http\Response;
class JsonApiException extends AppException
{
private $params = [
"message" => "",
"code" => 0,
"previous" => null,
"api" => [
"message" => "",
"code" => "",
"status" => 200
]
];
public function __construct(array $params)
{
$this->params = array_merge_recursive($this->params, $params);
parent::__construct($this->params["message"], 0, $this->params["previous"]);
}
public function getApiMessage() {
return $this->params["api"]["message"];
}
public function getApiCode() {
return $this->params["api"]["code"];
}
public function getHttpStatusCode() {
return $this->params["api"]["status"];
}
public function shouldBeLogged() {
return false;
}
public function log(Container $container, Request $request) {
if(!$this->shouldBeLogged()) return;
$logger = $container->get('logger.info');
$logger->info($this, array_merge($request->getHeaders(), [
"method" => $_SERVER["REQUEST_METHOD"],
"time" => $_SERVER["REQUEST_TIME"],
"query_string" => $_SERVER["QUERY_STRING"],
"host" => $_SERVER["HTTP_HOST"],
"referer" => $_SERVER["HTTP_REFERER"],
"user_agent" => $_SERVER["HTTP_USER_AGENT"],
"ip" => $_SERVER["REMOTE_ADDR"],
"uri" => $_SERVER["REQUEST_URI"]
]));
}
public function httpRespond(Response $response) {
return $response->withJson([
"error" => true,
"errors" => [
"server" => [[
"code" => $this->getApiCode(),
"message" => $this->getApiMessage()
]]
]
], $this->getHttpStatusCode());
}
}
然后我将其用作所有 JSON 错误的基本异常。我正在使用以下错误让客户知道它提供的用于注册的电子邮件地址已经存在。
namespace App\Exceptions\Validation\User;
use App\Exceptions\JsonApiException;
class EmailAlreadyUsedException extends JsonApiException
{
public function __construct()
{
parent::__construct([
"message" => "The e-mail provided by the exception has already been used",
"api" => [
"message" => "The provided e-mail address has already been used",
"code" => "EmailAlreadyUsed"
],
"previous" => null
]);
}
}
每当发生错误时,我都会将其添加到另一个自定义异常中,以便在验证期间同时响应多个错误:
namespace App\Exceptions;
use Slim\Http\Response;
class JsonApiMultipleException extends JsonApiException
{
private $httpStatusCode = 200;
private $exceptions = [];
public function __construct($httpStatusCode = 200, \Exception $previous = null)
{
parent::__construct([]);
$this->httpStatusCode = 200;
}
public function setHttpStatusCode(int $code) {
$this->httpStatusCode = $code;
}
public function add(string $param, JsonApiException $exception) {
if(!array_key_exists($param, $this->exceptions)) {
$this->exceptions[$param] = [];
}
$this->exceptions[$param][] = $exception;
}
public function length() {
$len = 0;
foreach ($this->exceptions as $param => $exceptions) {
$len += count($exceptions);
}
return $len;
}
public function map() {
$mapped = [];
foreach ($this->exceptions as $param => $exceptions) {
$mapped[$param] = array_map(function (JsonApiException $exception) {
return [
"code" => $exception->getApiCode(),
"message" => $exception->getApiMessage()
];
}, $exceptions);
}
return $mapped;
}
public function getExceptions() {
return $this->exceptions;
}
public function httpRespond(Response $response)
{
return $response->withJson([
"error" => true,
"errors" => $this->map()
], $this->httpStatusCode);
}
}
但是当我在验证期间抛出这个异常时 () :
namespace App\Validators;
use App\Exceptions\Validation\User\EmailAlreadyUsedException;
use App\Exceptions\Validation\User\InvalidEmailException;
use App\Exceptions\Validation\User\InvalidFirstNameException;
use App\Exceptions\Validation\User\InvalidLastNameException;
use App\Exceptions\Validation\User\InvalidPasswordException;
use Respect\Validation\Validator as v;
class UserValidator extends Validator
{
//...
private function validateEmail() {
//Validate e-mail
if(!v::stringType()->email()->validate($this->user->getEmail())) {
$this->exception->add('email', new InvalidEmailException());
}
//Check if e-mail already used
if(\UserQuery::create()->filterByEmail($this->user->getEmail())->count() > 0) {
$this->exception->add('email', new EmailAlreadyUsedException());
}
}
//...
}
它抛出以下异常:
[Thu Jun 30 05:42:47 2016] Slim Application Error:
Type: Error
Message: Wrong parameters for App\Exceptions\Validation\User\EmailAlreadyUsedException([string $message [, long $code [, Throwable $previous = NULL]]])
File: /var/www/ElectroAbhi/app/Exceptions/JsonApiException.php
Line: 33
Trace: #0 /var/www/ElectroAbhi/app/Exceptions/JsonApiException.php(33): Exception->__construct(Array, 0, Array)
#1 /var/www/ElectroAbhi/app/Exceptions/Validation/User/EmailAlreadyUsedException.php(19): App\Exceptions\JsonApiException->__construct(Array)
#2 /var/www/ElectroAbhi/app/Validators/UserValidator.php(58): App\Exceptions\Validation\User\EmailAlreadyUsedException->__construct()
#3 /var/www/ElectroAbhi/app/Validators/UserValidator.php(32): App\Validators\UserValidator->validateEmail()
#4 /var/www/ElectroAbhi/app/Routes/API/User/Create.php(39): App\Validators\UserValidator->validate()
#5 [internal function]: App\Routes\API\User\Create->__invoke(Object(Slim\Http\Request), Object(Slim\Http\Response), Array)
#6 /var/www/ElectroAbhi/vendor/slim/slim/Slim/Handlers/Strategies/RequestResponse.php(41): call_user_func(Object(App\Routes\API\User\Create), Object(Slim\Http\Request), Object(Slim\Http\Response), Array)
#7 /var/www/ElectroAbhi/vendor/slim/slim/Slim/Route.php(325): Slim\Handlers\Strategies\RequestResponse->__invoke(Object(App\Routes\API\User\Create), Object(Slim\Http\Request), Object(Slim\Http\Response), Array)
#8 /var/www/ElectroAbhi/vendor/slim/slim/Slim/MiddlewareAwareTrait.php(116): Slim\Route->__invoke(Object(Slim\Http\Request), Object(Slim\Http\Response))
#9 /var/www/ElectroAbhi/vendor/slim/slim/Slim/Route.php(297): Slim\Route->callMiddlewareStack(Object(Slim\Http\Request), Object(Slim\Http\Response))
#10 /var/www/ElectroAbhi/vendor/slim/slim/Slim/App.php(443): Slim\Route->run(Object(Slim\Http\Request), Object(Slim\Http\Response))
#11 /var/www/ElectroAbhi/vendor/slim/slim/Slim/MiddlewareAwareTrait.php(116): Slim\App->__invoke(Object(Slim\Http\Request), Object(Slim\Http\Response))
#12 /var/www/ElectroAbhi/vendor/slim/slim/Slim/App.php(337): Slim\App->callMiddlewareStack(Object(Slim\Http\Request), Object(Slim\Http\Response))
#13 /var/www/ElectroAbhi/vendor/slim/slim/Slim/App.php(298): Slim\App->process(Object(Slim\Http\Request), Object(Slim\Http\Response))
#14 /var/www/ElectroAbhi/public/index.php(105): Slim\App->run()
#15 {main}
我真的很困惑,当我在 EmailAlreadyUsedException
中明确实现了不带参数的构造函数时,它如何显示定义为 EmailAlreadyUsedException([string $message [, long $code [, Throwable $previous = NULL]]])
?
更新
我试图调试并找出为什么 $this->params["message"] 在 JsonApiException
的构造函数中是一个数组,但现在我更困惑了:
class JsonApiException extends AppException
{
private $params = [
"message" => "",
"code" => 0,
"previous" => null,
"api" => [
"message" => "",
"code" => "",
"status" => 200
]
];
public function __construct(array $params)
{
print_r($params);
die;
$this->params = array_merge_recursive($this->params, $params);
parent::__construct($this->params["message"], 0, $this->params["previous"]);
}
}
结果 -> Array ()
虽然我路过
parent::__construct([
"message" => "The e-mail provided by the exception has already been used",
"api" => [
"message" => "The provided e-mail address has already been used",
"code" => "EmailAlreadyUsed"
],
"previous" => null
]);
从 EmailAlreadyUsedException
到 JsonApiException
的构造函数,$params 数组似乎是空的。我是不是又漏掉了什么?
你所有的异常 class 都会调用 parent::__construct
。这意味着最终将调用 \Exception
class 构造函数。您没有为该构造函数提供正确的参数。
您可以从堆栈跟踪中看出:
Trace: #0 /var/www/ElectroAbhi/app/Exceptions/JsonApiException.php(33): Exception->__construct(Array, 0, Array)
你的 EmailAlreadyUsedException
调用 JsonApiException
的构造函数,它调用 PHP 的原生 \Exception
的构造函数 (Array, 0, Array)
,这不是构造函数期望什么。
你必须修复这两行:
$this->params = array_merge_recursive($this->params, $params);
parent::__construct($this->params["message"], 0, $this->params["previous"]);
显然,$this->params["message"]
和 $this->params["previous"]
是数组。但是你传递给 parent::__construct
的参数必须匹配签名
[string $message [, long $code [, Throwable $previous = NULL]]]
覆盖派生 class 中的构造函数不会覆盖父 class 中的构造函数。
经过长时间的调试,故障不在 OOP 行为中。它在我用来合并 JsonApiException
中的默认参数的函数中。我没有使用 array_merge_recursive
而 merges the elements of one or more arrays together so that the values of one are appended to the end of the previous one
,而是使用了 array_replace_recursive
.
当使用 array_merge_recursive
时,JsonApiException
的 $params 属性 被赋值为:
Array
(
[message] => Array
(
[0] =>
[1] => The e-mail provided by the exception has already been used
)
[code] => Array
(
[0] => 0
[1] => 0
)
[api] => Array
(
[message] => Array
(
[0] =>
[1] => The provided e-mail address has already been used
)
[code] => Array
(
[0] =>
[1] => EmailAlreadyUsed
)
[status] => Array
(
[0] => 200
[1] =>
)
)
)
然而,当使用 array_replace_recursive
时,$params 属性 变为:
Array
(
[message] => The e-mail provided by the exception has already been used
[code] => 0
[previous] =>
[api] => Array
(
[message] => The provided e-mail address has already been used
[code] => EmailAlreadyUsed
[status] => 200
)
)
这又为 \Exception::__construct()
提供了正确的参数。
只有一件事很奇怪,虽然它有效,但当我尝试这样做时:
class JsonApiException extends AppException
{
private $params = [
"message" => "",
"code" => 0,
"previous" => null,
"api" => [
"message" => "",
"code" => "",
"status" => 200
]
];
public function __construct(array $params)
{
print_r($params);
die;
$this->params = array_replace_recursive($this->params, $params);
parent::__construct($this->params["message"], 0, $this->params["previous"]);
}
}
我仍然得到 Array ()
,这很奇怪,因为 array_replace_recursive
设置了正确的值。
我正在 PHP 7.0 (Ubuntu 16.04) 上使用 Slim Framework 构建 REST API。为了正确处理异常,我扩展了基础 \Exception
class 如下:
namespace App\Exceptions;
class AppException extends \Exception
{
}
然后我将其用作所有应用程序异常的基本异常。对于所有向用户提供 JSON 响应的异常,我写了另一个 class :
namespace App\Exceptions;
use Slim\Container;
use Slim\Http\Request;
use Slim\Http\Response;
class JsonApiException extends AppException
{
private $params = [
"message" => "",
"code" => 0,
"previous" => null,
"api" => [
"message" => "",
"code" => "",
"status" => 200
]
];
public function __construct(array $params)
{
$this->params = array_merge_recursive($this->params, $params);
parent::__construct($this->params["message"], 0, $this->params["previous"]);
}
public function getApiMessage() {
return $this->params["api"]["message"];
}
public function getApiCode() {
return $this->params["api"]["code"];
}
public function getHttpStatusCode() {
return $this->params["api"]["status"];
}
public function shouldBeLogged() {
return false;
}
public function log(Container $container, Request $request) {
if(!$this->shouldBeLogged()) return;
$logger = $container->get('logger.info');
$logger->info($this, array_merge($request->getHeaders(), [
"method" => $_SERVER["REQUEST_METHOD"],
"time" => $_SERVER["REQUEST_TIME"],
"query_string" => $_SERVER["QUERY_STRING"],
"host" => $_SERVER["HTTP_HOST"],
"referer" => $_SERVER["HTTP_REFERER"],
"user_agent" => $_SERVER["HTTP_USER_AGENT"],
"ip" => $_SERVER["REMOTE_ADDR"],
"uri" => $_SERVER["REQUEST_URI"]
]));
}
public function httpRespond(Response $response) {
return $response->withJson([
"error" => true,
"errors" => [
"server" => [[
"code" => $this->getApiCode(),
"message" => $this->getApiMessage()
]]
]
], $this->getHttpStatusCode());
}
}
然后我将其用作所有 JSON 错误的基本异常。我正在使用以下错误让客户知道它提供的用于注册的电子邮件地址已经存在。
namespace App\Exceptions\Validation\User;
use App\Exceptions\JsonApiException;
class EmailAlreadyUsedException extends JsonApiException
{
public function __construct()
{
parent::__construct([
"message" => "The e-mail provided by the exception has already been used",
"api" => [
"message" => "The provided e-mail address has already been used",
"code" => "EmailAlreadyUsed"
],
"previous" => null
]);
}
}
每当发生错误时,我都会将其添加到另一个自定义异常中,以便在验证期间同时响应多个错误:
namespace App\Exceptions;
use Slim\Http\Response;
class JsonApiMultipleException extends JsonApiException
{
private $httpStatusCode = 200;
private $exceptions = [];
public function __construct($httpStatusCode = 200, \Exception $previous = null)
{
parent::__construct([]);
$this->httpStatusCode = 200;
}
public function setHttpStatusCode(int $code) {
$this->httpStatusCode = $code;
}
public function add(string $param, JsonApiException $exception) {
if(!array_key_exists($param, $this->exceptions)) {
$this->exceptions[$param] = [];
}
$this->exceptions[$param][] = $exception;
}
public function length() {
$len = 0;
foreach ($this->exceptions as $param => $exceptions) {
$len += count($exceptions);
}
return $len;
}
public function map() {
$mapped = [];
foreach ($this->exceptions as $param => $exceptions) {
$mapped[$param] = array_map(function (JsonApiException $exception) {
return [
"code" => $exception->getApiCode(),
"message" => $exception->getApiMessage()
];
}, $exceptions);
}
return $mapped;
}
public function getExceptions() {
return $this->exceptions;
}
public function httpRespond(Response $response)
{
return $response->withJson([
"error" => true,
"errors" => $this->map()
], $this->httpStatusCode);
}
}
但是当我在验证期间抛出这个异常时 () :
namespace App\Validators;
use App\Exceptions\Validation\User\EmailAlreadyUsedException;
use App\Exceptions\Validation\User\InvalidEmailException;
use App\Exceptions\Validation\User\InvalidFirstNameException;
use App\Exceptions\Validation\User\InvalidLastNameException;
use App\Exceptions\Validation\User\InvalidPasswordException;
use Respect\Validation\Validator as v;
class UserValidator extends Validator
{
//...
private function validateEmail() {
//Validate e-mail
if(!v::stringType()->email()->validate($this->user->getEmail())) {
$this->exception->add('email', new InvalidEmailException());
}
//Check if e-mail already used
if(\UserQuery::create()->filterByEmail($this->user->getEmail())->count() > 0) {
$this->exception->add('email', new EmailAlreadyUsedException());
}
}
//...
}
它抛出以下异常:
[Thu Jun 30 05:42:47 2016] Slim Application Error:
Type: Error
Message: Wrong parameters for App\Exceptions\Validation\User\EmailAlreadyUsedException([string $message [, long $code [, Throwable $previous = NULL]]])
File: /var/www/ElectroAbhi/app/Exceptions/JsonApiException.php
Line: 33
Trace: #0 /var/www/ElectroAbhi/app/Exceptions/JsonApiException.php(33): Exception->__construct(Array, 0, Array)
#1 /var/www/ElectroAbhi/app/Exceptions/Validation/User/EmailAlreadyUsedException.php(19): App\Exceptions\JsonApiException->__construct(Array)
#2 /var/www/ElectroAbhi/app/Validators/UserValidator.php(58): App\Exceptions\Validation\User\EmailAlreadyUsedException->__construct()
#3 /var/www/ElectroAbhi/app/Validators/UserValidator.php(32): App\Validators\UserValidator->validateEmail()
#4 /var/www/ElectroAbhi/app/Routes/API/User/Create.php(39): App\Validators\UserValidator->validate()
#5 [internal function]: App\Routes\API\User\Create->__invoke(Object(Slim\Http\Request), Object(Slim\Http\Response), Array)
#6 /var/www/ElectroAbhi/vendor/slim/slim/Slim/Handlers/Strategies/RequestResponse.php(41): call_user_func(Object(App\Routes\API\User\Create), Object(Slim\Http\Request), Object(Slim\Http\Response), Array)
#7 /var/www/ElectroAbhi/vendor/slim/slim/Slim/Route.php(325): Slim\Handlers\Strategies\RequestResponse->__invoke(Object(App\Routes\API\User\Create), Object(Slim\Http\Request), Object(Slim\Http\Response), Array)
#8 /var/www/ElectroAbhi/vendor/slim/slim/Slim/MiddlewareAwareTrait.php(116): Slim\Route->__invoke(Object(Slim\Http\Request), Object(Slim\Http\Response))
#9 /var/www/ElectroAbhi/vendor/slim/slim/Slim/Route.php(297): Slim\Route->callMiddlewareStack(Object(Slim\Http\Request), Object(Slim\Http\Response))
#10 /var/www/ElectroAbhi/vendor/slim/slim/Slim/App.php(443): Slim\Route->run(Object(Slim\Http\Request), Object(Slim\Http\Response))
#11 /var/www/ElectroAbhi/vendor/slim/slim/Slim/MiddlewareAwareTrait.php(116): Slim\App->__invoke(Object(Slim\Http\Request), Object(Slim\Http\Response))
#12 /var/www/ElectroAbhi/vendor/slim/slim/Slim/App.php(337): Slim\App->callMiddlewareStack(Object(Slim\Http\Request), Object(Slim\Http\Response))
#13 /var/www/ElectroAbhi/vendor/slim/slim/Slim/App.php(298): Slim\App->process(Object(Slim\Http\Request), Object(Slim\Http\Response))
#14 /var/www/ElectroAbhi/public/index.php(105): Slim\App->run()
#15 {main}
我真的很困惑,当我在 EmailAlreadyUsedException
中明确实现了不带参数的构造函数时,它如何显示定义为 EmailAlreadyUsedException([string $message [, long $code [, Throwable $previous = NULL]]])
?
更新
我试图调试并找出为什么 $this->params["message"] 在 JsonApiException
的构造函数中是一个数组,但现在我更困惑了:
class JsonApiException extends AppException
{
private $params = [
"message" => "",
"code" => 0,
"previous" => null,
"api" => [
"message" => "",
"code" => "",
"status" => 200
]
];
public function __construct(array $params)
{
print_r($params);
die;
$this->params = array_merge_recursive($this->params, $params);
parent::__construct($this->params["message"], 0, $this->params["previous"]);
}
}
结果 -> Array ()
虽然我路过
parent::__construct([
"message" => "The e-mail provided by the exception has already been used",
"api" => [
"message" => "The provided e-mail address has already been used",
"code" => "EmailAlreadyUsed"
],
"previous" => null
]);
从 EmailAlreadyUsedException
到 JsonApiException
的构造函数,$params 数组似乎是空的。我是不是又漏掉了什么?
你所有的异常 class 都会调用 parent::__construct
。这意味着最终将调用 \Exception
class 构造函数。您没有为该构造函数提供正确的参数。
您可以从堆栈跟踪中看出:
Trace: #0 /var/www/ElectroAbhi/app/Exceptions/JsonApiException.php(33): Exception->__construct(Array, 0, Array)
你的 EmailAlreadyUsedException
调用 JsonApiException
的构造函数,它调用 PHP 的原生 \Exception
的构造函数 (Array, 0, Array)
,这不是构造函数期望什么。
你必须修复这两行:
$this->params = array_merge_recursive($this->params, $params);
parent::__construct($this->params["message"], 0, $this->params["previous"]);
显然,$this->params["message"]
和 $this->params["previous"]
是数组。但是你传递给 parent::__construct
的参数必须匹配签名
[string $message [, long $code [, Throwable $previous = NULL]]]
覆盖派生 class 中的构造函数不会覆盖父 class 中的构造函数。
经过长时间的调试,故障不在 OOP 行为中。它在我用来合并 JsonApiException
中的默认参数的函数中。我没有使用 array_merge_recursive
而 merges the elements of one or more arrays together so that the values of one are appended to the end of the previous one
,而是使用了 array_replace_recursive
.
当使用 array_merge_recursive
时,JsonApiException
的 $params 属性 被赋值为:
Array
(
[message] => Array
(
[0] =>
[1] => The e-mail provided by the exception has already been used
)
[code] => Array
(
[0] => 0
[1] => 0
)
[api] => Array
(
[message] => Array
(
[0] =>
[1] => The provided e-mail address has already been used
)
[code] => Array
(
[0] =>
[1] => EmailAlreadyUsed
)
[status] => Array
(
[0] => 200
[1] =>
)
)
)
然而,当使用 array_replace_recursive
时,$params 属性 变为:
Array
(
[message] => The e-mail provided by the exception has already been used
[code] => 0
[previous] =>
[api] => Array
(
[message] => The provided e-mail address has already been used
[code] => EmailAlreadyUsed
[status] => 200
)
)
这又为 \Exception::__construct()
提供了正确的参数。
只有一件事很奇怪,虽然它有效,但当我尝试这样做时:
class JsonApiException extends AppException
{
private $params = [
"message" => "",
"code" => 0,
"previous" => null,
"api" => [
"message" => "",
"code" => "",
"status" => 200
]
];
public function __construct(array $params)
{
print_r($params);
die;
$this->params = array_replace_recursive($this->params, $params);
parent::__construct($this->params["message"], 0, $this->params["previous"]);
}
}
我仍然得到 Array ()
,这很奇怪,因为 array_replace_recursive
设置了正确的值。