bapcat/propifier

改进PHP属性的特质

3.1 2023-10-12 17:47 UTC

This package is auto-updated.

Last update: 2024-09-12 20:03:17 UTC


README

Build Status Coverage Status License

Propifier

一个为PHP添加真正面向对象属性支持的特质。

安装

Composer

Composer 是安装所有BapCat包的推荐方法。

$ composer require bapcat/propifier

GitHub

可以从 GitHub 下载BapCat包。

PHP "属性"的问题

从像.NET这样的其他语言过来的任何人可能会习惯于定义属性(访问器和修改器)来控制对类私有变量的访问。不幸的是,PHP缺少对这项有用特性的支持。有几种解决方案...

公共属性

class Foo {
  public $a;
  public $b;
}

使用公共属性是最简单的,但完全缺乏封装性。您无法控制谁可以更改对象的内部状态。

__get__set

class Foo {
  private $a;
  private $b;
  
  public function __get($name) {
    if(isset($this->$name)) {
      return $this->$name;
    }
    
    throw new Exception('Invalid property!');
  }
  
  public function __set($name, $value) {
    switch($name) {
      case 'a':
        $this->a = $value;
      break;
      
      case 'b':
        throw new Exception('b is read-only!');
    }
    
    throw new Exception('Invalid property!');
  }
}

使用PHP的后期绑定支持可以控制对对象可读和可写的内容,但牺牲了可读性、效率和类型提示。而且,不使用其他解决方案,这种方法也无法控制数组的读写访问。

...
  private $array = [];
...
  public function __get($name) {
    if(isset($this->$name)) {
      return $this->$name;
    }
  }
  
  public function __set($name, $value) {
    throw new Exception('You can\'t set me!');
  }
...

$foo = new FooWithArrayThatCantBeSet();

$foo->array['a'] = 'Test'; // Note: no exception
echo $foo->array['a']; // -> 'Test'

获取器和设置器

class Foo {
  private $a;
  private $b;
  private $array = [];
  
  public function getA() {
    return $this->a;
  }
  
  public function setA(A $a) {
    $this->a = $a;
  }
  
  public function getB() {
    return $this->b;
  }
  
  public function getOnlyOneArrayValue($index) {
    return $this->array[$index];
  }
}

使用Java风格的获取器和设置器是PHP中实现属性的最佳方法之一,但仍然存在缺陷。它非常冗长

$a = $foo->getA(); // rather than $foo->a

您还必须放弃使用数组访问语法来访问数组属性

$one_array_value = $foo->getOnlyOneArrayValue(1); // rather than $foo->array[1]

Propifier方式

Propifier解决了这些问题中的每一个。

class Foo {
  use \BapCat\Propifier\PropifierTrait;
  
  private $a;
  private $b;
  private $array = [];
  
  public function __construct() {
    $a = null;
    $b = new B();
    $array['test'] = 'Test';
  }
  
  protected function getA() {
    return $this->a;
  }
  
  protected function setA(A $a) { // Type hinting
    $this->a = $a;
  }
  
  protected function getB() {
    return $this->b;
  }
  
  // Controlled access
  //protected function setB(B $b) {
  //  $this->b = $b;
  //}
  
  // Propifier automatically detects arrays, and
  // allows array access when using the property
  protected function getArray($index) {
    return $this->array[$index];
  }
  
  // You can even define iterators to add foreach support
  protected function itrArray() {
    return new ArrayIterator($this->array);
  }
}
$foo = new Foo();

echo $foo->a; // -> null
$foo->a = new A(); // $a == new instance of A

echo $foo->b; // -> instance of B
$foo->b = new B(); // exception

echo $foo->array['test']; // -> 'Test'
$foo->array = []; // exception
$foo->array[1] = 'Test?'; // exception

foreach($foo->array as $key => $value) {
  // ...
}

效率

Propifier将使您在编写重要代码时更加高效,并且与类似解决方案不同,Propifier从一开始就被设计成快速。它一开始就计算出所有内容,并维护所有对象的属性静态映射,因此使用它们总是很快。