Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用ArrayObject在PHP中强制执行数组结构?_Php_Arrays_Arrayobject - Fatal编程技术网

使用ArrayObject在PHP中强制执行数组结构?

使用ArrayObject在PHP中强制执行数组结构?,php,arrays,arrayobject,Php,Arrays,Arrayobject,我有一组数据~30个属性,所有属性都有自己的值数组,我希望将这些值传递给PHP中的各个类,我还希望强制执行数据的数组结构。多个类将期望此结构保持一致 由于这些事实,我不能真正依赖于标准数组,所以我决定传递一个对象。我查看了ArrayObject,虽然它允许我将类设置为一个数组,但我没有看到任何关于强制结构的内容 是否有一个现有的标准类可以处理其类似数组的结构的强制执行,同时仍然被视为数组,例如,基本上是ArrayObject+强制执行 阵列结构的一个示例: $item_type_props =

我有一组数据~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个类,它们只是一堆属性,对于仅仅保存数据来说似乎有些过分


或者,如果我只是从错误的心态来看待这个问题,并且遗漏了一些非常非常明显的东西,那么请指出它。我觉得我是,但我的大脑可能正在休假。

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

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

$validator = Validation::createValidator();

$constraint = new Assert\Collection(array(
    // the keys correspond to the keys in the input array
    'name' => new Assert\Collection(array(
      'first_name' => new Assert\Length(array('min' => 101)),
      'last_name' => new Assert\Length(array('min' => 1)),
    )),
    'email' => new Assert\Email(),
    'simple' => new Assert\Length(array('min' => 102)),
    'gender' => new Assert\Choice(array(3, 4)),
    'file' => new Assert\File(),
    'password' => new Assert\Length(array('min' => 60)),
));

$violations = $validator->validate($input, $constraint);
这让您可以将如何验证的细节推到另一个已经测试过的级别,同时让您的代码关注它为什么需要这些数据。对于Symfony的情况,您可以使用阵列作为存储机制,并使用一种设计,即防火墙不通过验证数据进行验证

我们可以这样做的一个方法是不带任何意义的。假设我们已经实现了一个方法,可能是使用Symfony的验证器来返回一个经过验证的数组。我们可以使用匈牙利符号表示我们的结构已通过验证且安全:

<?php
$vInput = validate($_GET); // Hungarian notation: any variable beginning with "v" is "validated" and safe to use

function foobar(array $vInput) { ... }

您可能希望使此实现成为一个抽象基类,具体子体实现constraints方法以提供对数组对象本身的特定限制。这为获取专用数据传输对象提供了一种灵活的方法。

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

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

$validator = Validation::createValidator();

$constraint = new Assert\Collection(array(
    // the keys correspond to the keys in the input array
    'name' => new Assert\Collection(array(
      'first_name' => new Assert\Length(array('min' => 101)),
      'last_name' => new Assert\Length(array('min' => 1)),
    )),
    'email' => new Assert\Email(),
    'simple' => new Assert\Length(array('min' => 102)),
    'gender' => new Assert\Choice(array(3, 4)),
    'file' => new Assert\File(),
    'password' => new Assert\Length(array('min' => 60)),
));

$violations = $validator->validate($input, $constraint);
这让您可以将如何验证的细节推到另一个已经测试过的级别,同时让您的代码关注它为什么需要这些数据。对于Symfony的情况,您可以使用阵列作为存储机制,并使用一种设计,即防火墙不通过验证数据进行验证

我们可以这样做的一个方法是不带任何意义的。假设我们已经实现了一个方法,可能是使用Symfony的验证器来返回一个经过验证的数组。我们可以使用匈牙利符号表示我们的结构已通过验证且安全:

<?php
$vInput = validate($_GET); // Hungarian notation: any variable beginning with "v" is "validated" and safe to use

function foobar(array $vInput) { ... }

您可能希望使此实现成为一个抽象基类,具体子体实现constraints方法以提供对数组对象本身的特定限制。这为获取专用数据传输对象提供了一种灵活的方法。

一般来说,我认为,除非您将数据传递到另一个上下文,例如javascript,否则PHP应用程序应该很好地组织在PHP类中。这是实施该结构最简单的方法。您是对的,这可能会导致使用一组getter和setter的非常简单的DTO,但它肯定会优于检查数组结构。在您的例子中,数组中似乎也有一个关系,否则将它们组合到一个数组中就毫无意义了

使用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是一个值对象,它本身实际上是一个很小的类,强制使用它:

class PhoneNumber {
    private $phoneNumber;

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

        $this->phoneNumber = $phoneNumber;
     }
}
这也是验证可以与@bishop的答案相结合的地方,因为您可以使用现有的验证器来帮助您。您可以在这里找到一个电话号码的值对象示例,只需通过谷歌搜索即可:

我确实觉得您可能出于另一个原因将PHP数据转换为数组?比如与数据库交互,或者将其传递到不同的上下文,比如Javascript

在这种情况下,一旦你的DTO和VO被明确定义,你就可以考虑将它们序列化到JSON中。您可以使用Symfony库进行此操作,如下所述:

如果你真的想要数组,你也可以考虑将它们水淹到实体,使用Marco Pivetta ocramius的库,后者是水化的权威。 在学说中广泛使用的oach:

有很多选择,嗯

不过,老实说,IMHO通常需要一个很好的参数来传递这些复杂的数组,因为数组在支持功能方面提供的很少。此外,您的代码很可能也很快变得难以读取和维护,因为在每一个需要对数组进行某种修改或使用其数据元素的地方,您都需要执行@bishop所描述的各种检查或运行验证。我不允许这样的东西存在于我们的应用程序代码库中

因此,总结一下:如果您有一组定义严密的PHP对象,这些对象具有完善的构造函数、属性、getter、setter、构造函数、关系、操作方法以及某种序列化机制,那么您的状态就很好了。另外,其他开发人员将“被迫”使用对象和提供的接口方法和VO,从而保证一定程度的可维护性和质量


顺便说一下,你可以考虑一下Martin Fowler关于这些事情的想法:

一般来说,我会说,除非你将数据传递到另一个上下文,例如JavaScript——PHP应用程序应该在PHP类中很好地组织起来。这是实施该结构最简单的方法。您是对的,这可能会导致使用一组getter和setter的非常简单的DTO,但它肯定会优于检查数组结构。在您的例子中,数组中似乎也有一个关系,否则将它们组合到一个数组中就毫无意义了

使用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是一个值对象,它本身实际上是一个很小的类,强制使用它:

class PhoneNumber {
    private $phoneNumber;

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

        $this->phoneNumber = $phoneNumber;
     }
}
这也是验证可以与@bishop的答案相结合的地方,因为您可以使用现有的验证器来帮助您。您可以在这里找到一个电话号码的值对象示例,只需通过谷歌搜索即可:

我确实觉得您可能出于另一个原因将PHP数据转换为数组?比如与数据库交互,或者将其传递到不同的上下文,比如Javascript

在这种情况下,一旦你的DTO和VO被明确定义,你就可以考虑将它们序列化到JSON中。您可以使用Symfony库进行此操作,如下所述:

如果你真的想要数组,你也可以考虑将它们从实体中提取出来,使用Marco Pivetta ocramius的库,它是水化的权威。 有很多选择,嗯

不过,老实说,IMHO通常需要一个很好的参数来传递这些复杂的数组,因为数组在支持功能方面提供的很少。此外,您的代码很可能也很快变得难以读取和维护,因为在每一个需要对数组进行某种修改或使用其数据元素的地方,您都需要执行@bishop所描述的各种检查或运行验证。我不允许这样的东西存在于我们的应用程序代码库中

因此,总结一下:如果您有一组定义严密的PHP对象,这些对象具有完善的构造函数、属性、getter、setter、构造函数、关系、操作方法以及某种序列化机制,那么您的状态就很好了。另外,其他开发人员将“被迫”使用对象和提供的接口方法和VO,从而保证一定程度的可维护性和质量


顺便说一下,你可以考虑一下Martin Fowler对这些事情的想法:

是的,我一直坚持错误的方法。我一直在想什么时候验证会把我的想法带到正确的位置@bishop您是否建议使用一个扩展ArrayObject的类和一个消费类可以调用的validate方法,该方法依次告诉它们正在读取的数据是完全结构化的true/false,并且存在错误?这是一种方法。匈牙利符号是另一种。不过,有很多方法可以做到这一点。最好的方法实际上取决于您现有的代码库。将一些想法编辑成答案。最后,我将你的@gaidinaman答案结合起来。我创建了一个集合类,用validator扩展ArrayObject,以检查集合是否具有所需的键。我为集合中的item_类型创建了值对象,并使用Ocramius的Hydrator快速填充这些对象。我使用一个循环来水合并将每个水合项目添加到集合中;你

提到的是无效的。你是说$constraints=$this->constraints['fields'][$index];?听起来不错。我没有编译或测试,只是近似于必需的代码。是的,我被错误的方法卡住了。我一直在想什么时候验证会把我的想法带到正确的位置@bishop您是否建议使用一个扩展ArrayObject的类和一个消费类可以调用的validate方法,该方法依次告诉它们正在读取的数据是完全结构化的true/false,并且存在错误?这是一种方法。匈牙利符号是另一种。不过,有很多方法可以做到这一点。最好的方法实际上取决于您现有的代码库。将一些想法编辑成答案。最后,我将你的@gaidinaman答案结合起来。我创建了一个集合类,用validator扩展ArrayObject,以检查集合是否具有所需的键。我为集合中的item_类型创建了值对象,并使用Ocramius的Hydrator快速填充这些对象。我使用一个循环来水合并将每个水合项目添加到集合中;你提到的是无效的。你是说$constraints=$this->constraints['fields'][$index];?听起来不错。我没有编译或测试,只是近似于必需的代码。深思熟虑的回答。谢谢。欢迎来到SO!谢谢@bishop,说实话,我该开始为社区做出贡献了。。。我是“干净代码”概念的忠实粉丝,因此我坚持一个相当严格的开发实践,我也教给我的团队。如果你感兴趣,我会推荐这本书:事实上,我是在传递数据。在数据库中,它存储在id、项目类型、项目属性和值的4列表中。DB和PHP中的数据结构是不同的。我曾考虑过$a->getPhone->hasBluetooth,但从$\u POST到类的整个设置/获取导致了对ArrayObject的关注。我想你可能正在这样做,在开发与数据库或API交互的应用程序时,这是一个常见的挑战。现在,如果这只是一个小项目,而您是唯一的维护人员,我会说为您做任何工作。但如果是别的,我总是会选择将“数据库内容”与“应用程序内容”分离,而不是让来自一个领域的挑战在另一个领域给您带来问题。换句话说:从数据库中获取数据,并将其转换为“有意义”的PHP对象,即解耦/接口。。。作为一个事后的想法:只需考虑如果不解耦,那么数据库更改可能会传播到整个代码库中。如果进行解耦,则必须更改从数据库到对象的映射,但对象本身将保持不变……这是一个深思熟虑的答案。谢谢。欢迎来到SO!谢谢@bishop,说实话,我该开始为社区做出贡献了。。。我是“干净代码”概念的忠实粉丝,因此我坚持一个相当严格的开发实践,我也教给我的团队。如果你感兴趣,我会推荐这本书:事实上,我是在传递数据。在数据库中,它存储在id、项目类型、项目属性和值的4列表中。DB和PHP中的数据结构是不同的。我曾考虑过$a->getPhone->hasBluetooth,但从$\u POST到类的整个设置/获取导致了对ArrayObject的关注。我想你可能正在这样做,在开发与数据库或API交互的应用程序时,这是一个常见的挑战。现在,如果这只是一个小项目,而您是唯一的维护人员,我会说为您做任何工作。但如果是别的,我总是会选择将“数据库内容”与“应用程序内容”分离,而不是让来自一个领域的挑战在另一个领域给您带来问题。换句话说:从数据库中获取数据,并将其转换为“有意义”的PHP对象,即解耦/接口。。。作为一个事后的想法:只需考虑如果不解耦,那么数据库更改可能会传播到整个代码库中。如果进行解耦,则必须更改从数据库到对象的映射,但对象本身将保持不变……如果最终生成约30个验证定义,则类可能不会太多。最后,它将与创建~30个类相同,但您也将使用PHP,而不是使用它。@apokryfos,对不起,您能为我澄清一下使用PHP吗?PHP将强制执行您定义的类的规则,因为它允许OOP语法。如果您编写类,那么PHP语言将很有帮助,但是如果您使用ArrayObject,那么毫无疑问,您最终会遇到一些限制,因为在我看来,ArrayObject一开始就有点黑客。如果你想要一个中间地带,也许可以实现一些自我验证结构。当然,这也有助于检查数据来自何处。如果它来自API,那么
有可能在某个地方存在该API的服务描述符,因此您可以使用该描述符自动生成类。如果您最终创建了约30个验证定义,则类可能不会太过复杂。最后,它将与创建~30个类相同,但您也将使用PHP,而不是使用它。@apokryfos,对不起,您能为我澄清一下使用PHP吗?PHP将强制执行您定义的类的规则,因为它允许OOP语法。如果您编写类,那么PHP语言将很有帮助,但是如果您使用ArrayObject,那么毫无疑问,您最终会遇到一些限制,因为在我看来,ArrayObject一开始就有点黑客。如果你想要一个中间地带,也许可以实现一些自我验证结构。当然,这也有助于检查数据来自何处。如果它来自一个API,那么就有可能在某个地方存在该API的服务描述符,这样您就可以使用它来自动生成类。