我有一些用于应用程序的 PHP 片段,我试图限制来自 JavaScript 应用程序前端请求的输入。该页面使用 JSON 对象发送请求,该对象包含我指定为“打开”、“完成”或“关闭”的字段值。我想防止不必要的输入篡改或发送值。
问题:
下面的属性 $eventstatus 是用枚举类型提示的,但是当我在 $array['EventStatus']
中分配字符串值时,PHP (7.4.9) 报告了我的类型不兼容的错误。它需要查看 Status 类型,而实际上我正在为其分配一个字符串。
我该如何解决这个问题?
$event->eventstatus = $array['EventStatus'];
枚举类(状态)
<?php
namespace app\enums;
abstract class Status
{
const Open = 'Open';
const Complete = 'Complete';
const Closed = 'Closed';
}
Mapper 类成员函数 - 片段,下面的代码采用数组值并将其映射到类属性
<?php
function mapFromArray($event, $array) {
if (!is_null($array['EventStatus'])) $event->eventstatus = $array['EventStatus'];
}
模型类
<?php
namespace data\model;
use app\enums\Status;
class Event
{
public $eventid;
public $riskid;
public $eventtitle;
public Status $eventstatus;
}
答案 0 :(得分:1)
您的类型提示实际上告诉 PHP 您希望 $eventstatus
是 Status
的实例。但这些值实际上只是简单的字符串:'Open'
、'Complete'
和 'Closed'
。
所以正确的类型提示是:
<?php
namespace data\model;
use app\enums\Status;
class Event
{
// ...
public string $eventstatus;
}
但是有了这个 PHP 接受 any string
而不仅仅是一个“有效”的。在这里使用适当的枚举会有所帮助,但目前 PHP 7 没有对枚举的本机支持(即 implemented for PHP 8.1 though)。
如果您想使用 Status
类以获得更易读的代码,您只需将类型提示更改为 string
。
如果你想验证输入数据,你可以像这样扩展代码:
<?php
namespace app\enums;
abstract class Status
{
const Open = 'Open';
const Complete = 'Complete';
const Closed = 'Closed';
const Valid_Statuses = [
self::Open,
self::Complete,
self::Closed,
];
}
function mapFromArray($event, $array) {
if (!is_null($array['EventStatus'])) {
if (in_array($array['EventStatus'], Status::Valid_Statuses)) {
$event->eventstatus = $array['EventStatus'];
} else {
// handle invalid status value here
}
}
}
如果你想使用严格的类型提示来确保任何地方的有效性,你需要将值包装到类的实例中,例如:
namespace app\enums;
abstract class Status
{
const Open = 'Open';
const Complete = 'Complete';
const Closed = 'Closed';
const Valid_Statuses = [
self::Open,
self::Complete,
self::Closed,
];
private string $value;
public function __construct(string $value) {
if (!in_array($value, self::Valid_Statuses)) {
throw \InvalidArgumentException(sprintf('Invalid status "%s"', $value));
}
$this->value = $value;
}
public function getValue(): string {
return $this->value;
}
public function __toString(): string {
return $this->value;
}
}
function mapFromArray($event, $array) {
if (!is_null($array['EventStatus'])) {
try {
$event->eventstatus = new Status($array['EventStatus']);
} catch (\Exception $exception) {
// handle invalid status value here
}
}
}
答案 1 :(得分:0)
我尝试了一种与使用数组值建议的方法略有不同的方法,但仍然依赖某种数组来检查允许的值。
在我的 Events 类中,我从抽象类 Mapper 扩展而来(我在其中添加了一个新的 performMapping 函数以使映射更加动态)
<?php
namespace data\mapper;
use app\enums\Status;
use data\model\Event;
class Events extends Mapper
{
public function mapFromArray($array) : Event
{
$event = $this->_performMapping($array, new Event());
return $event;
}
}
模型 - 添加魔法方法 (__set, __get)
<?php
namespace data\model;
use app\enums\Status;
class Event
{
public $eventid;
public $riskid;
public $eventtitle;
private $eventstatus;
public $eventownerid;
public $actualdate;
public $scheduledate;
public $baselinedate;
public $actuallikelihood;
public $actualtechnical;
public $actualschedule;
public $actualcost;
public $scheduledlikelihood;
public $scheduledtechnical;
public $scheduledschedule;
public $scheduledcost;
public $baselinelikelihood;
public $baselinetechnical;
public $baselineschedule;
public $baselinecost;
public function __set($name, $value)
{
switch ($name)
{
case 'eventstatus':
{
$class = Status::class;
try
{
$reflection = new \ReflectionClass($class);
}
catch (\ReflectionException $ex)
{
return null;
}
$constants = $reflection->getConstants();
if (array_key_exists($value, $constants))
$this->$name = constant("\\".$class."::$constants[$value]");
else
throw (new \Exception("Property $name not found in " . $class));
}
default:
{
if (property_exists(get_class($this), $name))
$this->$name = $value;
else
throw (new \Exception("Property $name not found in " . get_class($this)));
}
}
}
public function __get($name)
{
switch ($name)
{
case 'eventstatus':
return $this->$name;
default:
if (property_exists($this, $name))
return $this->$name;
else
return null;
}
}
}
映射器
<?php
namespace data\mapper;
abstract class mapper
{
protected $db = null;
public function __construct(\PDO $db)
{
$this->db = $db;
}
abstract public function mapFromArray($array);
protected function _populateFromCollection($results = null)
{
$return = [];
if ($results != null)
{
foreach($results as $result)
{
$return[] = $this->mapFromArray($result);
}
}
return $return;
}
protected function _performMapping($array, $object)
{
foreach (array_keys($array) as $property)
{
$lowerCaseProperty = strtolower($property);
if (property_exists(get_class($object), $property))
$object->$property = $array[$property];
else if (property_exists(get_class($object), $lowerCaseProperty))
$object->$lowerCaseProperty = $array[$property];
}
return $object;
}
枚举
<?php
namespace app\enums;
abstract class Status
{
const Open = 'Open';
const Complete = 'Complete';
const Closed = 'Closed';
}