我有一组数据(~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个类只是一堆属性而且看起来有点过分了持有数据。
或者,如果我只是从错误的心态接近这个并且真的遗漏了一些东西,那么请将它指出来。我感觉自己是我,但我的大脑可能正在休假。
答案 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