icanboogie/prototype

实现原型设计模式;getter/setter

v5.1.0 2023-02-15 13:21 UTC

This package is auto-updated.

Last update: 2024-09-03 23:07:51 UTC


README

Release Code Quality Code Coverage Packagist

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 实例来定义原型方法。

使用全局配置定义原型方法

可以使用单个全局配置配置所有原型。对于每个类,您可以定义原型实现的方法。

以下示例演示了如何为 CatFierceCat 类的实例定义 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() 方法递归地将实例转换为数组,在这种情况下,实现 ToArrayToArrayRecursive 的树的所有实例都将转换为数组。

获取对象的 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 模式获取对象时创建实例的方式相同,即实例的属性在调用其构造函数之前设置。

异常

以下定义了以下异常

持续集成

该项目通过 GitHub actions 持续进行测试。

Tests Static Analysis Code Style

行为准则

本项目遵循 贡献者行为准则。通过参与此项目和其社区,您应遵守此准则。

贡献

请参阅 CONTRIBUTING 了解详细信息。

许可证

icanboogie/prototype 采用 BSD-3-Clause 许可证发布。