zf2分析:Zend/EventManager(Part1)

Zend/EventManager,开发人员可以使用它来实现事件-监听器的机制, 从本篇开始将对其进行详细介绍。

先理解何谓事件/监听器机制,以及简易实现:
事件是一个点的概念,比如一个读操作完成前和完成后的两个状态就是两个事件;
监听器是一个执行过程,可以是一个匿名函数,也可以是一个对象的某个方法;
下面将举例解释事件和监听器的抽象演变;

思考如下功能:读取一个用户的用户名,先从内存(比如memache)读取,如果内存中没有,则到数据库(比如mysql)中读取,并在读出后将数据写入到内存。

1. 常规的实现
传统的代码如下,也可到这里查看代码eventManager_demo1.php

<?php
/**
 * 模拟数据库
 */
class DB
{
    // 模拟存储在数据库的数据
    private static $users = array(
            '1' => 'username1',
            '2' => 'username2',
            '3' => 'usermame3',
    );
    
    static function getUserName($userId)
    {
        // 模拟数据库查询
        return self::$users[$userId];
    }
}

/**
 * 模拟缓存
 */
class Cache
{
    private static $users = array();
    
    static function getUserName($userId)
    {
        return isset(self::$users[$userId]) ? self::$users[$userId] : '';
    }
    
    static function setUserName($userId, $userName)
    {
        self::$users[$userId] = $userName;
    }
}

class User
{
    private $userId;

    public function __construct($userId)
    {
        $this->userId = $userId;
    }
    public function getName()
    {
        // step1 先从内存读取用户名,如果存在就直接返回用户名
        $userName = Cache::getUserName($this->userId);
        if ($userName) {
            return $userName;
        }
        
        // step2 从数据库读取用户名
        $userName = DB::getUserName($this->userId);
        
        // step3 将用户名写入内存
        Cache::setUserName($this->userId, $userName);

        // step4
        return $userName;
    }
}
$user = new User('1');
var_dump($user->getName());
var_dump($user->getName());

/*
运行结果:
read from Cache: 
read from DB: username1
write to Cache: username1
string(9) "username1"
read from Cache: username1
string(9) "username1"
 */
?>

以上代码在User::getName($userId)中,实现了功能主体逻辑;

2. 引入事件/监听器机制
现在我想使我的系统更灵活一些,比如外面在调用User::getName($userId)时,同样可以获取到期望的结果,但是在User::getName($userId)内部,不再去具体的去写和操作Cache有关的代码,这时候我们引入事件和监听器。

我们为step1定义一个名为’readCacheEvent’的事件,和名为’readCacheListener’的监听器;
为step3定义一个名为’writeCacheEvent’的事件,和名为’writeCacheListener’的监听器;
我要的效果是在User::getName($userId)内部去触发定义的事件后,对应的监听器就会执行。
我们引入一个简易版的EventManager来协助完成这个效果,

class EventManager
{
    /**
     * 按对应关系存储事件和监听器
     * @var array
     */
    private static $events = array();
    
    /**
     * 绑定事件
     * 按对应关系存储事件和监听器
     * @param string $eventName
     * @param callable $listener
     */
    public static function attach($eventName, $listener)
    {
        self::$events[$eventName][] = $listener;
    }
    
    /**
     * 触发事件
     * 根据对应的事件名称去执行监听器
     * @param string $eventName
     * @param array $argv
     * @return <mixed>
     */
    public static function trigger($eventName, $argv = array())
    {
        $listeners = self::$events[$eventName];
        $response = array();
        foreach ($listeners as $listener) {
            $response[] = call_user_func($listener, $argv);
        }
        
        return $response;
    }
    
}

这样我们有了负责绑定事件,触发事件的EventManager,我们再为事件的监听器创建一个对象来集中实现事件监听器的逻辑:

class CacheListener
{
    static function readCacheListener($argv)
    {
        return Cache::getUserName($argv['userId']);
    }
    
    static function writeCacheListener($argv)
    {
        Cache::setUserName($argv['userId'], $argv['userName']);
    }
}

效果马上就要出来了,现在需要把原来的User类中step1和step3的逻辑代码替换成事件触发的代码,替换后如下:

class User
{
    private $userId;

    public function __construct($userId)
    {
        $this->userId = $userId;
    }
    public function getName()
    {
        // step1 触发事件
        $results = EventManager::trigger('readCacheEvent', array('userId' => $this->userId));
        $userName = $results[count($results) - 1];
        if ($userName) {
            return $userName;
        }
        
        // step2 从数据库读取用户名
        $userName = DB::getUserName($this->userId);
        
        // step3 触发事件
        EventManager::trigger('writeCacheEvent', array('userId' => $this->userId, 'userName' => $userName));

        // step4
        return $userName;
    }
}

ok, 最后一步,我们在程序运行开始时将监听器注册到相应的事件上,如下,完整代码请到这里查看eventManager_demo2.php

// 对事件注册监听器
EventManager::attach('readCacheEvent', 'CacheListener::readCacheListener');
EventManager::attach('writeCacheEvent', 'CacheListener::writeCacheListener');

$user = new User('1');
var_dump($user->getName());
var_dump($user->getName());

/*
运行结果:
read from Cache: 
read from DB: username1
write to Cache: username1
string(9) "username1"
read from Cache: username1
string(9) "username1"
 */

3. 总结:一个事件/监听器工具要考虑的几个方面:
a. 事件和监听器的对应关系, 如何注册监听器,触发事件
在EventManager内部我们使用一个二维数组$events来保存事件和监听器的对应关系
b. 在触发事件时,如何将当前上下文和参数传递给对应的监听器
在User内部触发事件时,把上下文参数传递给监听器
c. 在执行一个事件上的多个监听器时,对每个监听器的返回结果的处理和保存
在EventManager内部我们使用数组$response来保存每个监听器的返回结果,最后把$response返回个触发这个事件的函数比如User::getName(),由它决定如何处理$response.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值