【问题标题】:PHP - Assigning a string value to a variable that is type hinted as an enumPHP - 将字符串值分配给类型提示为枚举的变量
【发布时间】:2021-07-25 20:24:09
【问题描述】:

我有一些 PHP sn-ps 用于我试图限制来自 JavaScript 应用程序前端请求的输入的应用程序。该页面使用 JSON 对象发送请求,该对象包含我指定为“打开”、“完成”或“关闭”的字段值。我想防止不必要的输入篡改或值被发送。

问题:

下面的属性 $eventstatus 是用枚举提示的类型,但是当我在 $array['EventStatus'] PHP (7.4.9) 中分配字符串值时,会报告我的类型不兼容的错误。实际上我正在为其分配一个字符串时,它需要查看一个状态类型。
我该如何解决这个问题?

$event->eventstatus = $array['EventStatus'];   

枚举类(状态)

<?php
    namespace app\enums;

    abstract class Status
    {
        const Open = 'Open';
        const Complete = 'Complete';
        const Closed = 'Closed';
    }

Mapper 类成员函数 - sn-p,下面的代码接受一个数组值并将其映射到类属性

<?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;
    }

【问题讨论】:

  • $array['EventStatus'] 是什么?
  • 目前我已经根据输入字段中值的完成和单击提交按钮为其分配了一个字符串值“打开”、“完成”或“关闭”。 $array 包含来自前端应用程序的 JSON 值。我的目标是防止将无效的字符串值发送到服务器。
  • 这能回答你的问题吗? Enumerations on PHP
  • 为什么不使用包含有效值的简单数组并使用 in_array()Status 不是 string

标签: php


【解决方案1】:

您的类型提示实际上告诉 PHP 您希望 $eventstatusStatusinstance。但这些值实际上只是简单的字符串:'Open''Complete''Closed'。 所以正确的类型提示是:

<?php
    namespace data\model;
    use app\enums\Status;

    class Event
    {
        // ...
        public string $eventstatus;
    }

但是有了这个 PHP 接受 any string 而不仅仅是一个“有效”的。在这里使用正确的 Enums 会有所帮助,但目前 PHP 7 没有对 Enums 的原生支持(即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
        }
    }
}

【讨论】:

    【解决方案2】:

    我尝试了一种与使用数组值提出的方法略有不同的方法,但仍然依赖某种数组来检查允许的值。

    在我的 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';    
        }
        
                
    

    【讨论】:

    • 不清楚是否将 $eventstatus 设为私有将阻止智能感知使用魔术方法显示成员变量。这是我需要研究的。如果我在 $eventstatus 之类的私有属性中引入了不必要的问题,我愿意提供避免智能感知问题的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2018-11-01
    • 1970-01-01
    • 2017-10-13
    • 1970-01-01
    • 1970-01-01
    • 2014-09-20
    • 2018-02-14
    • 1970-01-01
    相关资源
    最近更新 更多