【问题标题】:Is there any way to define the structure of Array or Object in PHP?有没有办法在 PHP 中定义 Array 或 Object 的结构?
【发布时间】:2017-09-21 11:26:11
【问题描述】:

我的问题和标题说的基本一模一样。

我在玩 TypeScript 有一段时间了,有一种简单的方法可以定义对象的结构,即定义接口内部的属性。我知道 PHP 不支持接口中的属性,但是有没有办法以某种方式定义我传递的对象的结构(不使用一些抽象类)或至少是数组(需要在里面显示哪些键) .

我的意思是:

// I already sanitized that this method returns the exact same structure every time 
$data = $this->storage->get($some); 
// here I'm passing the data I obtained to my Builder
Builder::createFromArray($data); 
// or
Builder::create($data);

class Builder {
    public static function createFromArray(\ArrayOfSomeType $array) {}
    public static function create(\ObjectOfSomeTypeWithPropertiesSpecified $obj) {}
}

希望我解释清楚。

【问题讨论】:

  • 为什么不使用类?
  • 您正在努力从 PHP 中的另一种语言复制一种语言结构。我认为这种方法是有缺陷的。 PHP 是一门不同的语言,从头开始。阅读如何使用它。例如这里:php-fig.org
  • 没有开箱即用的解决方案。您可以使用json schema 定义数组的结构并将其应用于 PHP 数组(json_encode / decode 等)。您已经熟悉 JS,所以我认为没有问题。 Json Schema 也非常简单。
  • 您可能会打开stdClass,因为它是一个泛型类类型,但它不用于与 OO 相关的目的。但在其中包含数据很有用,几乎与使用 JSON 一样。
  • 感谢大家的建议。我可能会创建一个模型类,它能够从我传递给构造函数的数组中创建自己,然后我将创建我将它注入到我的 Builder 类中。我想这样使用会更干净。 @KIKOSoftware 我没有努力,我只是想问是否有任何方法可以做到这一点,因为它更干净而且我在文档中找不到任何内容,感谢您提供的链接,看起来不错。

标签: php arrays object php-7


【解决方案1】:

正如您所说,PHP 中没有任何概念可以完全满足您的需求。有一个ArrayObject 类,可以满足您对具有松散数量成员的对象的需求。

class Builder {
    public function create(iterable $data) : \ArrayObject {
        $object = (new \ArrayObject())->setFlags(\ArrayObject::ARRAY_AS_PROPS);
        foreach ($data as $key => $value) {
            $object->offsetSet($key, $value);
        }

        return $object;
    }
}

}

示例 1:使用数组填充对象

$object = Builder::create([ 'bla' => 'blubb', 'yadda' => 'blubb' ]);
var_dump($object->bla);

构建器返回一个对象,其属性与数组的键完全相同。所有属性都包含数组具有的值。您可以使用 foreachIterator 对象迭代新对象。

示例 2:用另一个对象填充对象

$class = new \stdClass();
$class->propertyA = 'B';
$class->propertyB = 'B';

$object = Builder::Create($class);
var_dump($object->propertyA);

使用 PHP,几乎所有对象都是可迭代的。这意味着您可以遍历一个对象的属性并将它们传递给我们的ArrayObject 实例。

用 PHP 补水的常用方法

PHP 中还有另一种方法,称为水合。它比显示的示例要复杂一些,但如果你明白的话,它非常方便。

// your entity / data model
class Car implements EntityInterface {
    protected $horsepower;

    public function getHorsepower() : int
    {
        if ($this->horsepower === null) {
            $this->horsepower = 0;
        }

        return $this->horsepower;
    }

    public function setHorsepower(int $horsepower) : self
    {
        $this->horsepower = $horsepower;
        return $this;
    }
}

这是我们汽车类型的数据模型(实体)。我们的 Car 继承自一个接口。这只是用于水合器中的类型提示原因。我们的汽车有一个特性,叫做马力。当然,汽车可以有更多的属性。但这只是举例。 ;)

class ClassMethodsHydrator
{
    public function hydrate(array $data, EntityInterface $entity) : EntityInterface {
        foreach ($data as $key => $value) {
            $methodName = 'set' . ucwords(strtolower($key));
            if (method_exists($entity, $methodName) {
                $entity->{$methodName}($value);
            }
        }

        return $entity;
    }
}

这是我们的小型保湿器示例。这个类是做什么的,你的构建器做什么。但以更具体的方式。如果它与实体的方法匹配,它会获取一个实体并用给定的数据对其进行水合。

$entity = (new Hydrator())->hydrate(
    [ 
        'horsepower' => 172,
        'notexistingproperty' => 'bla', 
    ],
    new Car()
);

var_dump($entity->getHorsepower()); // 172

补水的好处很简单。您只有具有已知属性的模型。这很安全,您随时都知道您的模型可以为您做什么。例如,汽车有方向舵是没有意义的。在这种情况下,汽车只能得到汽车得到的东西。就像在示例中汽车只占用数组的马力成员。

【讨论】:

    【解决方案2】:

    我认为你可以通过在界面中创建 getter 和 setter 来强制属性。然后代替

    $item->something;
    

    你可以使用

    $item->getSomething();
    

    【讨论】:

    • 其实这是一个很好的方法。但您的示例可能更详细、更具描述性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-03
    • 2015-07-12
    • 1970-01-01
    • 2021-10-19
    • 1970-01-01
    • 1970-01-01
    • 2014-01-24
    相关资源
    最近更新 更多