siriusphp/stratum 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

siriusphp/stratum

Composer 安装命令:

composer require siriusphp/stratum

包简介

General purpose library that allows extending the functionality of your objects through a decorator-like mechanism

README 文档

README

#Sirius Stratum

Build Status Code Coverage Scrutinizer Code Quality

Sirius\Stratum is a library that will allow you to create extensible systems without having to use:

  1. Deep inheritance. Inherintance can take you not that far when it comes to create extensible classes.
  2. Traits. Traits overcome the limitations of inherintance but they require to define the structure up-front (you cannot add traits at run-time)
  3. Event systems. Event systems require you to write lots of code for everything that requires interaction with the event system.
  4. Command busses. Command busses allow you to change the behaviour of the system by having different functions respond to a command but composing these function is very easy. Think of a getLatestPosts command that should retrieve the posts from database; you attach a callback to that command but later you want to implement a cache mechanism (ie: query the cache first and delegate to the previous callback if items are not in cache)
  5. AOP (Aspect Oriented Programming). AOP is difficult because of the terminology and implementations are "heavy". http://go.aopphp.com/ is ones of such implementations

Having said that, I must warn you that the Sirius\Stratum is not flawless and has some trade-offs (very small).

How does it work?

The project started with the question: how can you make a method of a class change its behaviour at run-time while preserving the inheritance? The obvious answer is wrapping an object inside other similar to an onion. From this point of view Sirius\Stratum is similar to the decorator pattern. Imagine you have an ORM and you want to:

  1. Log the calls (for benchmarking purposes)
  2. Intercept exceptions to send notifications to the developer
  3. Cache the results
$orm = new CacheBehaviour(new LogBehaviour( new ExceptionNotifier(new ORM($dbConn))));
$orm->getLatestArticles(); 

This is how one would implement this using the decorator pattern. There are some limitations/issues with this approach

  1. If at the bottom of the callstack the ORM object calls another of its methods (eg: getLatestArticles() calls $this->executeQuery()) the call to that method will not go through the layers above
  2. Your decorators will have to implement the same interface of the decorated class. Granted this can be automated (ie: have a mechanism to automatically create the decorator class on disk)

Now enter Sirius\Stratum!

1. Move your code into a "Base" class

class ORMBase {
	
	function __construct($dbConn) {
		// whatever...
	}
	
	function getLatestArticles() {
		// query the database, map the results, return a collection
	}
}

class ORM extends ORMBase {
	use \Sirius\Stratum\LayerableTrait;
}

Note! For PHP5.3 you'll need to copy&paste the code from the \Sirius\Stratum\LayerableTrait trait yourself. Sorry!

2. Decide which methods you want to be extendable/decoratable

class ORM extends ORMBase {
	use \Sirius\Stratum\LayerableTrait;
	
	function getLatestArticles() {
		return $this->executeLayeredMethod(__FUNCTION__, func_get_args());
	}
}

3. Instruct the Stratum Manager what layers to add to the target class

$manager = new Sirius\Stratum\Manager();
$manager->add('CacheBehaviour', 'ORM', -1000); // -1000 is the priority (not mandatory though)
$manager->add('LogBehaviour', 'ORM', 999);
$manager->add('ExceptionNotifier', 'ORM', 998);

// add decorators by TRAIT
$manager->add('LogBehaviour', 'uses:Vendor\Package\LoggableTrait');

// add decorators by INTERFACE
$manager->add('LogBehaviour', 'implements:Vendor\Package\LoggableInterface');

// add decorator by PARENT CLASS
$manager->add('LogBehaviour', 'extends:Vendor\Package\SomeBaseClass');

// attach the layers on the target method
$ormInstance->setTopLayer($manager->createLayerStack($ormInstance));

The layers classes must extend the Sirius\Stratum\Layer class.

That's it!

FAQ?

1. What are the trade-offs?

  1. It is not a "pure" implementation of a pattern. People complained that it is either the Mediator pattern, the Chain of Responsibility pattern or a disquised Event pattern.
  2. You have global state (ie: a singleton manager of the "strata" for each class). I consider the implementation to be similar to having traits (at the global level you define the traits) and I am not a purist.
  3. Since the layers (or "decorators") are not required to implement the interface of the decorated object the library relies on __call() to pass the calls to the next layers, which come with a performance penalty. I will try to address this issue in the future though.

2. Am I limited to classes for behaviour?

No. You can add an object as a decorator (the object will be cloned whenever needed by that class though, so keep that in mind) or a callback/function that returns a decorator.

$manager->add($someAlreadyInstanciatedLayer, 'ORM');
$manager->add($someCallableThatReturnsALayer, 'ORM');

1. What happens if the decorators have the same priority?

They will be called in the reverse order they where added (ie: the last will wrap around the first).

$manager->add('LayerA', 'LayerableClass', 100);
$manager->add('LayerB', 'LayerableClass', 100);

$decoratedClassObject->foo();

Assuming those are the only decorators DecoratorB::foo() will be called first which might call LayerA::foo() which might call LayerableClass::foo()

4. Can I add a decorator multiple times?

Yes. The manager doesn't check if a decorator is attached to a class so be careful.

5. Can I still use events?

Yes. You can have a decorator that will emit events. It might even make your life easier (use the same decorator on ALL the classes where you need that).

class EventsLayer extends Sirius\Stratum\Layer {

	function foo() {
		$this->emit('before_foo', func_get_args());
		return $this->callNext(__FUNCTION__, func_get_args());
	}

}

6. Since I like decorators so much can I decorate a decorator?

Haven't tested it yet but I don't see why not.

siriusphp/stratum 适用场景与选型建议

siriusphp/stratum 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 5 次下载、GitHub Stars 达 7, 最近一次更新时间为 2014 年 12 月 18 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「aop」 「mixins」 「design patterns」 「aspect oriented programming」 「decorator pattern」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 siriusphp/stratum 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 siriusphp/stratum 我们能提供哪些服务?
定制开发 / 二次开发

基于 siriusphp/stratum 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 5
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 8
  • 点击次数: 19
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 7
  • Watchers: 4
  • Forks: 1
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2014-12-18