PHP中使用ArrayObject的可执行数组结构?

时间:2017-11-20 17:51:24

标签: php arrays arrayobject

我有一组数据(~30个属性,都有自己的值数组),我想传递给PHP中的各个类,我想强制执行数据的数组结构。多个类将期望此结构保持一致。

由于这些事实我不能真正依赖标准数组,因此我决定传递一个对象。我查看了ArrayObject,虽然它允许我设置/获取好像该类是一个数组,但我没有看到任何关于强制执行结构的内容。

是否有一个现有的标准类可以处理它类似数组的结构的强制执行,同时仍然被视为一个数组,例如,基本上是ArrayObject +强制执行?

阵列结构的一个例子:

$item_type_props = array(
    'phone'     => array('speed' => 1000, 'self_label' => false, 'support_poe' => true, 'bluetooth' => false),
    'pbx'       => array('acd_support' => true, 'max_conn' => 300, max_phones => 600),
    'adapter'   => array('fxo' => 4, 'fxs' => 0, 't1_e1_pri' => 0),
    etc...
);

我知道数组中的每个属性都可以是它自己的类,并通过构造函数和set / get强制执行它自己的字段但是后来我突然有~30个类只是一堆属性而且看起来有点过分了持有数据。

或者,如果我只是从错误的心态接近这个并且真的遗漏了一些东西,那么请将它指出来。我感觉自己是我,但我的大脑可能正在休假。

2 个答案:

答案 0 :(得分:2)

虽然您可以自己动手,但我鼓励您使用现有的验证实施。例如,Symfony\Validator允许您定义嵌套结构和每个级别的要求:

array

这使您可以将如何的详细信息推送到另一个(已经过测试的)级别,同时让您的代码专注于为什么它需要这些数据。对于Symfony案例,您可以使用<?php $vInput = validate($_GET); // Hungarian notation: any variable beginning with "v" is "validated" and safe to use function foobar(array $vInput) { ... } 作为存储机制,并使用防火墙未经验证的数据验证的设计。

我们可能会这样做的一种方式是符号。假设我们已经实现了一个方法,可能使用Symfony的验证器来返回一个经过验证的数组。我们可以使用匈牙利表示法来表明我们的结构已通过验证并且是“安全的”:

use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints as Assert;

class ValidatedArray extends \ArrayObject {
    public function construct($input = [], $flags = 0, $iterator_class = 'ArrayIterator') {
        $violations = Validation::createValidator()->validate($array, $this->constraints());
        // throw exception if any violations
        parent::__construct($input, $flags, $iterator_class);
    }

    public function __offsetSet($index, $value) {
        $constraints = $this->constraints()[$index]; // specific constraints to this index
        $violations = Validation::createValidator()->validate($array, $constraints);
        // throw exception on violations
        parent::__offsetSet($index, $value);
    }

    public function constraints() {
        return new Assert\Collection(...);
    }
}

$input = new ValidatedArray($_REQUEST); // first time validation
$input['foo'] = 'bar'; // also subject to validation

虽然这是高效的,但长期维护并不好。因此,您可以考虑一个允许您利用类型系统的对象包装器:

abstract

您可能希望将此实现作为constraints基类,具体后代实现Notification notification = (Notification) event.getParcelableData(); RemoteViews views = notification.contentView; Class secretClass = views.getClass(); try { Map<Integer, String> text = new HashMap<Integer, String>(); Field outerFields[] = secretClass.getDeclaredFields(); for (int i = 0; i < outerFields.length; i++) { if (!outerFields[i].getName().equals("mActions")) continue; outerFields[i].setAccessible(true); ArrayList<Object> actions = (ArrayList<Object>) outerFields[i] .get(views); for (Object action : actions) { Field innerFields[] = action.getClass().getDeclaredFields(); Object value = null; Integer type = null; Integer viewId = null; for (Field field : innerFields) { field.setAccessible(true); if (field.getName().equals("value")) { value = field.get(action); } else if (field.getName().equals("type")) { type = field.getInt(action); } else if (field.getName().equals("viewId")) { viewId = field.getInt(action); } } if (type == 9 || type == 10) { text.put(viewId, value.toString()); } } System.out.println("title is: " + text.get(16908310)); System.out.println("info is: " + text.get(16909082)); System.out.println("text is: " + text.get(16908358)); } } catch (Exception e) { e.printStackTrace(); } 方法以提供对数组对象本身的特定限制。这提供了一种获取专用数据传输对象的灵活方法。

答案 1 :(得分:2)

一般来说,我认为 - 除非您将数据传递到另一个上下文,例如javascript - PHP应用程序应该在PHP类中很好地组织。这是执行结构的最简单方法。你是对的,这可能会导致很多直接DTO与一堆getter和setter,但它肯定会击败检查数组结构。在你的情况下,数组中似乎也存在关系,否则将它们组合成数组是没有意义的。

使用PHP7,您可以清楚地定义方法签名并强制执行类型,例如

public function setSomething(string $myValue)
{
    $this->something = $myValue;
}

与返回类型相同:

public function myActionMethod(array $options): ActionRespsonse
{
    // Do something
}

如果您有更复杂的数据类型,我建议使用值对象。这些只是简单的PHP类,代表了更复杂的价值。例如,电话号码:

public function setPhoneNumber(PhoneNumber $phoneNumber)
{
    $this->phoneNumber = $phoneNumber;
}

这里的PhoneNumber是一个Value Object,它实际上是一个很小的类,它强制使用它:

class PhoneNumber {
    private $phoneNumber;

    public __construct(string $phoneNumber) {
        if (strlen($phoneNumber) != 10) {
            throw new \Exception('Not a valid phone number');
        }

        $this->phoneNumber = $phoneNumber;
     }
}

这也是验证可以与@bishop的答案相关联的地方,因为您可以使用现有的Validator来帮助您。你可以在这里找到一个电话号码的价值对象的例子(只需用Google搜索):Example Phone Number Value Object

我确实感觉您可能会因为其他原因将PHP数据转换为数组?喜欢与数据库交互,或将其传递给不同的上下文,例如Javascript?

在这种情况下,一旦你对DTO和VO进行了整齐定义,你就可以考虑将它们序列化,例如:来自JSON。您可以使用Symfony库,如下所述:Symfony Serializer

如果你真的想要阵列,你也可以考虑使用来自Marco Pivetta(ocramius)的图书来保持它们与实体之间的水合作用,这个库几乎是水化的权威,这是Doctrine中广泛使用的一种方法:Ocramius Hydrator

很多选择,呃!?

老实说,恕我直言,恕我直言,通常需要一个很好的论据来传递这些复杂的数组,因为数组提供的支持功能很少。此外,您的代码将很可能并且很快变得难以阅读和维护,因为在对阵列进行某种修改或使用其数据元素的每一点上,您都需要实现各种检查或运行@bishop所描述的验证。我不允许在我们的应用程序代码库中存在类似的东西...

所以,总结一下:如果你有一套严格定义的PHP对象,它们具有完善的构造函数,属性,getter,setter,构造函数,关系,动作方法以及某种序列化机制,那么你的状态就会很好。此外,其他开发人员将被“强制”使用对象并提供接口(方法和VO),从而保证一定程度的可维护性和质量。

顺便说一下,您可以考虑让Martin Fowler对这些事情有所了解:Martin Fowler: DTO Martin Fowler: VO Martin Fowler: DTO2