icanboogie / prototype
实现原型设计模式;getter/setter
Requires
- php: >=7.2.5
- ext-json: *
- icanboogie/accessor: ^4.0
Requires (Dev)
- phpstan/phpstan: ^1.9
- phpunit/phpunit: ^8.5
README
icanboogie/prototype 包允许使用 PrototypeTrait 在运行时定义类的成员方法,并且由于使用了 icanboogie/accessor 包,这也包括 getter 和 setter。
安装
composer require icanboogie/prototype
在运行时定义方法
可以使用类的 prototype 在运行时定义方法。这些方法立即对类的每个实例可用,并且被该类的子类继承。
<?php use ICanBoogie\Prototype; use ICanBoogie\PrototypeTrait; class Cat { use PrototypeTrait; } class OtherCat extends Cat {} class FierceCat extends Cat {} $cat = new Cat; $other_cat = new OtherCat; $fierce_cat = new FierceCat; $second_fierce_cat = new FierceCat; // define the 'meow' prototype method for Cat class Prototype::from(Cat::class)['meow'] = function(Cat $cat) { return 'Meow'; }; // override the 'meow' prototype method for FierceCat class Prototype::from(FierceCat::class)['meow'] = function(Cat $cat) { return 'MEOOOW !'; }; echo $cat->meow(); // Meow echo $other_cat->meow(); // Meow echo $fierce_cat->meow(); // MEOOOW ! echo $second_fierce_cat->meow(); // MEOOOW !
在运行时定义 getter 和 setter
由于 getter 和 setter 也是方法,它们的定义方法与常规方法相同。
<?php use ICanBoogie\Prototype; use ICanBoogie\PrototypeTrait; class TimeObject { use PrototypeTrait; public $seconds; } $time = new Time; $prototype = Prototype::from(Time::class); $prototype['set_minutes'] = function(Time $time, $minutes) { $time->seconds = $minutes * 60; }; $prototype['get_minutes'] = function(Time $time, $minutes) { return $time->seconds / 60; }; $time->seconds = 120; echo $time->minutes; // 2 $time->minutes = 4; echo $time->seconds; // 240
依赖注入,控制反转
可以使用原型的 lazy getter 实现依赖注入和控制反转。
以下示例演示了如何定义一个魔法 image
属性,以从 ActiveRecord 模型中延迟加载记录。
<?php use ICanBoogie\Prototype; use ICanBoogie\PrototypeTrait; class Article { use PrototypeTrait; public $image_id; } // … Prototype::from(Article::class)['get_image'] = function(Article $target) use ($image_model) { return $target->image_id ? $image_model[$target->image_id] : null; }; $article = new Article; $article->image_id = 12; echo $article->image->nid; // 12
原型方法和 parent::
原型方法可以被覆盖,并且可以像常规方法一样与 parent::
调用一起工作。
在以下示例中,原型方法 url()
被添加到 Node
类中,并在 News
类中覆盖,尽管如此,仍然可以在 News
中使用 parent::
。
<?php use ICanBoogie\Prototype; use ICanBoogie\PrototypeTrait; class Node { use PrototypeTrait; } class News extends Node { public function url($type) { return parent::url("another/$type"); } } Prototype::from(Node::class)['url'] = function($node, $type) { return "/path/to/$type.html"; }; $node = new Node; $news = new News; echo $node->url('madonna'); // /path/to/madonna.html echo $news->url('madonna'); // /path/to/another/madonna.html
定义原型方法
可以使用全局配置;通过 Prototyped
实例的 prototype
属性;或使用与 PrototypeTrait 特性一起使用的类的 Prototype
实例来定义原型方法。
使用全局配置定义原型方法
可以使用单个全局配置配置所有原型。对于每个类,您可以定义原型实现的方法。
以下示例演示了如何为 Cat
和 FierceCat
类的实例定义 meow()
方法。尽管示例中使用了闭包,但可以使用任何可调用的函数来定义方法,例如 "App\Hooks::cat_meow"
。
<?php ICanBoogie\Prototype::bind([ Cat::class => [ 'meow' => function(Cat $cat) { return 'Meow'; } ], FierceCat::class => [ 'meow' => function(FierceCat $cat) { return 'MEOOOW !'; } ] ]);
通过 prototype
属性定义原型方法
可以使用 prototype
属性定义原型方法
<?php use ICanBoogie\PrototypeTrait; class Cat { use PrototypeTrait; } $cat = new Cat; $cat->prototype['meow'] = function(Cat $cat) { return 'Meow'; }; echo $cat->meow();
使用原型实例定义原型方法
可以使用类的 Prototype
实例定义原型方法
<?php use ICanBoogie\Prototype; Prototype::from(Cat::class)['meow'] = function(Cat $cat) { return 'Meow'; };
使用配置片段定义原型方法
如果该包通过 ICanBoogie 使用 icanboogie/bind-prototype 绑定,则可以使用 prototype
配置片段定义原型方法
<?php use Article; // config/prototype.php return [ Article::class . '::url' => 'App\Hooks::article_url', Article::class . '::get_url' => 'App\Hooks::article_get_url' ];
获取对象的数组表示形式
可以使用 to_array()
方法获取 Prototyped
实例的数组表示形式。仅导出公共和外观属性。
<?php use ICanBoogie\Prototyped; class A extends Prototyped { public $a; protected $b; private $c; public function __construct($a, $b, $c) { $this->a = $a; $this->b = $b; $this->c = $c; } protected function get_c() { return $this->c; } protected function set_c($value) { $this->c = $value; } } $a = new A('a', 'b', 'c'); var_dump($a->to_array()); // array(2) { // ["a"]=> // string(1) "a" // ["c"]=> // string(1) "c" //}
如前所述,外观属性也被导出。应覆盖 to_array()
方法来更改此行为。
此外,还可以使用 to_array_recursive()
方法递归地将实例转换为数组,在这种情况下,实现 ToArray 或 ToArrayRecursive 的树的所有实例都将转换为数组。
获取对象的 JSON 表示形式
可以使用 to_json()
方法获取对象的 JSON 表示形式。该方法非常直观,它调用 to_array_recursive()
并将结果传递给 json_encode()
。
<?php echo $a->to_json(); // {"a":"a","c":"c"}
从属性数组创建实例
Prototyped::from()
方法可以从属性数组创建实例
<?php class A extends Prototyped { private $a; protected $b; public $c; } $a = A::from([ 'b' => 2, 'c' => 3 ]);
实例的创建方式与 PDO 在使用 FETCH_CLASS
模式获取对象时创建实例的方式相同,即实例的属性在调用其构造函数之前设置。
异常
以下定义了以下异常
- MethodNotDefined:在尝试访问未定义的方法时抛出的异常。
- MethodOutOfScope:在尝试调用超出作用域的方法时抛出的异常。
持续集成
该项目通过 GitHub actions 持续进行测试。
行为准则
本项目遵循 贡献者行为准则。通过参与此项目和其社区,您应遵守此准则。
贡献
请参阅 CONTRIBUTING 了解详细信息。
许可证
icanboogie/prototype 采用 BSD-3-Clause 许可证发布。