Symfony2学习笔记之事件分配器

----EventDispatcher组件使用

简介:

      面向对象编程已经在确保代码的可扩展性方面走过了很长一段路。它是通过创建一些责任明确的类,让它们之间变得更加灵活,开发者可以通过继承这些类创建子类,来改变它们的行为。但是如果想将某个开发者的改变跟其它已经编写了自己子类的开发者共享,这种面向对象的继承就不再那么有用了。

举一个现实的实例,你想为你的项目提供一个插件系统。一个能够被添加到方法的插件,或者在方法执行的前后完成某些工作,而不会干扰到其它插件。这个通过单一的继承完成不是一个容易的事情,多重继承又有它的局限性。

SF2的Event Dispathcer组件通过一个简单有效的方式实现了Mediator模式,让这些需求的实现成为可能并为你的项目带来了真正的扩展性。

从HttpKernel组件的示例说起,一旦一个Response对象被创建,能够让系统中其它元素在该Response对象被真正使用之前修改它将是非常有用的。(比如添加一个缓存的头),SF2内核通过一个事件kernel.response做到了这一点.

那么它是如何工作的呢?
一个listener告诉中心dispatcher对象它想监听kernel.response事件:
在某个点上,SF2核心告诉dispatcher对象分配kernel.response事件,同时传递一个Event对象给分配的目标对象。
该Event对象可以用于访问Response对象。
Dispatcher通知所有监听kernel.response事件的监听者,允许它们对Response对象进行修改。

如果一个事件要被分配,它必须有一个能够标识自己的唯一名字(比如:kernel.response),这样任意数量的监听者都可以注册监听该名字。在分配过程中,同时会创建一个Event实例传递给所有的监听者。该Event对象本身通常会包含一些关于被分配事件的数据。

关于事件的名字可以是任意字符串,但是通常遵循如下的规则:
只使用小写字符,数字和点号以及下划线。
用命名空间名加点号作为前缀。
通常以指定发生行为的动词作为名字的结尾(比如request).

如下的定义时合法的事件名:
kernel.response
form.pre_set_data

事件的名称和具体事件对象:
当Dispatcher通知一个监听者时,它会传递一个真正的Event对象给这些监听者。Event基类非常简单,它除了包含一个用于停止事件传递的方法外,其它什么都没有。

通常特定事件的数据需要和该事件一起被传递给监听者,让监听该事件的监听者拥有足够的信息来响应事件。比如在kernel.response事件中,一个Event对象被创建并传递给了监听它的每一位监听者,该Event实例的实际类型是FilterResponseEvent,是Event基类的一个子类。该类包含了像getResponse()和setResponse()类型的方法,允许监听者获取甚至替换Response对象。

这个故事的寓意是,当创建一个某一事件的监听者时,传递给监听者的Event对象可能是其特定的子类,该类有附加的方法来从事件中获取信息并回复该事件。

事件分配器Dispatcher:
它是整个事件分配系统的中心对象。
通常情况下,只有唯一的分配器被创建,它维护者注册于它的所有监听者。
当一个事件通过Dispatcher被分配时,它会通知所有注册监听该事件的监听者。

1 use Symfony\Component\EventDispatcher\EventDispatcher;
2
3 $dispatcher = new EventDispatcher();

将监听者注册到事件分配器:
要使用已有的事件,你需要把事件监听者关联到分配器以便它在分配事件时能够通知它们。
通过在dispatcher上面调用addListener()方法可以将任意的PHP合法调用关联到某个事件。

1 $listener = new AcmeListener();
2 $dispatcher->addListener(‘foo.action‘, array($listener,‘onFooAction‘));

这里addListener方法接收3个参数:

监听者需要监听的事件名称字符串作为第一个参数:
一个监听事件的PHP调用
一个可选参数代表监听程序执行优先级(越大代表越重要),它觉得着监听者被触发的顺序,默认值为0。如果两个监听者优先级值相同那么按照其注册顺序执行。

注意:PHP callable是一个PHP变量,它可以被用于call_user_func()方法并当它被传入is_callable()方法时会返回一个true。 它可以是\Closure实例,一个实现了__invoke方法的对象,一个表示一个函数方法的字符串,或者表示一个对象方法或者一个类方法的数组。

到目前为止你知道了那些PHP对象可以被注册为监听者。你还可以注册PHP Closure作为事件监听者:

1 use Symfony\Component\EventDispatcher\Event;
2
3 $dispatcher->addListener(‘foo.action‘,function(Event $event){
4     //该方法将在foo.action事件被分配时执行
5 });

一旦一个监听者被注册到dispatcher,它就会一直等待该事件被通知。

在上面的实例中,当foo.action被分配时,分配器会调用AcmeListener::onFooAction方法并传入Event对象作为唯一参数。

 1 use Symfony\Component\EventDispatcher\Event;
 2
 3 class AcmeListener
 4 {
 5      // ...
 6
 7      public function onFooAction(Event $event)
 8     {
 9          // ... 相关操作
10      }
11 }

在很多情况下则是Event对象的一些子类被传递给指定事件的监听者。这些子类会让监听者能够通过一些附加的方法访问关于该事件的特定信息。我们通常需要查看SF2提供的文档说明或者事件的实现来决定Event事件触发时需要传入的类。

比如:kernel.event事件传入一个Symfony\Component\HttpKernel\Event\FilterResponseEvent:

1 use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
2
3 public function onKernelResponse(FilterResponseEvent $event)
4 {
5     $response = $event->getResponse();
6     $request = $event->getRequest();
7
8     // ...
9 }

下面我们来看看创建并分配一个事件的过程:
我们除了注册监听者到已有的事件外,我们还可以创建和监听自己的事件。这对于我们创建第三方类库或者保持我们自己系统组件的灵活性和解耦分层有用。

1.首先创建静态事件类:
假设你想创建一个新事件store.order,它将会在每次订单被创建时分配。
为了让其看起来更规范,我们创建一个StoreEvents类用于定义和说明我们的事件:

 1 namespace Acme\StoreBundle;
 2
 3 final class StoreEvents
 4 {
 5     /**
 6     * store.order事件会在每次订单被创建时抛出
 7     *
 8     * 监听该事件的监听者会接收到一个
 9     * Acme\StoreBundle\Event\FilterOrderEvent实例
10     *
11     * @var string
12     */
13     const STORE_ORDER = ‘store_order‘;
14 }

注意,该类没有做任何实际的工作,它的目的仅仅是定位公用事件信息集中的地方。同时我们还注意到在注释里说明了一个FilterOrderEvent对象被一同传递给监听者。

2.创建一个Event对象
接下来,当你派遣一个新事件时,你需要创建一个Event实例并传递给dispatcher。dispatcher会传递该实例到每一个监听该事件的监听者那里。如果你不需要传递任何信息给这些监听者,你可以直接使用默认的Symfony\Component\EventDispatcher\Event类。
大多时候,你需要传递关于该事件的一些信息给监听者,要完成这个目的,你需要创建一个新的扩展于Symfony\Component\EventDispatcher\Event类的新类。

在该例子中,每个监听者需要方法一些模拟的Order对象。那么需要创建一个新的Event子类来满足:

 1 namespace Acme\StoreBundle\Event;
 2
 3 use Symfony\Component\EventDispatcher\Event;
 4 use Acme\StoreBundle\Order;
 5
 6 class FilterOrderEvent extends Event
 7 {
 8     protected $order;
 9
10     public function __construct(Order $order)
11     {
12         $this->order = $order;
13     }
14
15     public function getOrder()
16     {
17         return $this->order;
18     }
19 }

这样每个监听者都可以通过该类的getOrder方法来访问订单对象了。

3. 分配事件
dispatch()方法通知所有的给定事件的监听者。它带有两个参数:分配事件的名字和需要传递给每个监听者的Event实例。

 1 use Acme\StoreBundle\StoreEvents;
 2 use Acme\StoreBundle\Order;
 3 use Acme\StoreBundle\Event\FilterOrderEvent;
 4
 5 // 实例化一个需要的订单对象
 6 $order = new Order();
 7 // ...
 8
 9 // 创建 FilterOrderEvent 并分配它
10 $event = new FilterOrderEvent($order);
11 $dispatcher->dispatch(StoreEvents::STORE_ORDER, $event);

注意,这里是一个特定的FilterOrderEvent对象被创建并传递给该事件的所有监听者,监听者们接收该对象后通过其getOrder方法访问Order对象。

1 // 假设有一些监听者被注册到 "STORE_ORDER" 事件
2 use Acme\StoreBundle\Event\FilterOrderEvent;
3
4 public function onStoreOrder(FilterOrderEvent $event)
5 {
6     $order = $event->getOrder();
7     // 对订单进行一些处理
8 }

4.使用事件订阅者
最常见的方式是一个事件监听者通过dispatcher注册到某个事件,该监听者可以监听一个或者多个事件并且在每次该事件被分配时获得通知。

另外一种监听事件的方式是通过一个事件订阅者来完成。
一个事件订阅者是一个PHP类,它能够告诉dispatcher到底哪些事件应该订阅。
事件订阅者实现了EventSubscriberInterface接口,它唯一需要实现的一个静态方法叫 getSubscribedEvents
下面的示例显示一个事件订阅者订阅kernel.response和store.order事件:

 1 namespace Acme\StoreBundle\Event;
 2
 3 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 4 use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
 5
 6 class StoreSubscriber implements EventSubscriberInterface
 7 {
 8     public static function getSubscribedEvents()
 9     {
10         return array(
11             ‘kernel.response‘ => array(
12                 array(‘onKernelResponsePre‘, 10),
13                 array(‘onKernelResponseMid‘, 5),
14                 array(‘onKernelResponsePost‘, 0),
15             ),
16             ‘store.order‘ => array(‘onStoreOrder‘, 0),
17             );
18     }
19
20     public function onKernelResponsePre(FilterResponseEvent $event)
21     {
22         // ...
23     }
24
25     public function onKernelResponseMid(FilterResponseEvent $event)
26     {
27         // ...
28     }
29
30     public function onKernelResponsePost(FilterResponseEvent $event)
31     {
32         // ...
33     }
34
35     public function onStoreOrder(FilterOrderEvent $event)
36     {
37         // ...
38     }
39 }                        

它非常类似于监听者类,除了该类本身能够告诉dispatcher需要监听哪些事件除外。

要注册一个订阅者到dispatcher,需要使用dispatcher的addSubscriber()方法。

1 use Acme\StoreBundle\Event\StoreSubscriber;
2
3 $subscriber = new StoreSubscriber();
4 $dispatcher->addSubscriber($subscriber);

这里dispatcher会自动每一个订阅者的getSubscribedEvents方法返回的事件。该方法会返回一个以事件名字为索引的数组,它的值既可以是调用的方法名也可以是组合了方法名和调用优先级的数组。

上面的例子显示如何在订阅者类中注册多个监听方法到同一个事件,以及显示了如何为每个监听方法传入优先级设置。优先级数越高的方法越早被调用。
根据上面示例的定义,当kernel.response事件被分配时,其监听方法的调用顺序依次是:
onKernelResponsePre,OnKernelResponseMid和onKernelResponsePost.

5.阻止事件流/传递
有些情况下,可能有一个监听者来阻止其它监听者被调用。换句话说,监听者需要能告诉dispatcher来阻止将事件传递给后续的监听者。这个可以在一个监听者内部通过stopPropagation()方法来实现。

1 use Acme\StoreBundle\Event\FilterOrderEvent;
2
3 public function onStoreOrder(FilterOrderEvent $event)
4 {
5     // ...
6
7     $event->stopPropagation();
8 }

现在,任何还没有被调用的监听store.order事件的监听者将不会再被调用。

我们可以通过isPropagationStopped()方法来判断一个事件被阻止。

1 $dispatcher->dispatch(‘foo.event‘,$event);
2 if($event->isPropagationStopped()){
3     //..
4 }

6.事件分配器知道事件和监听者
EventDispatcher总是注入一个它自己的引用到传入的event对象。这就意味着所有的监听者可以通过Dispatcher传递给自己的Event对象的getDispatcher()方法直接访问EventDispatcher对象。

这些可以导致EventDispatcher的一些高级应用,包括将监听者派遣其它事件,事件链或者更多监听者的事件延迟加载到dispatcher对象。
下面是延迟加载监听者:

 1 use Symfony\Component\EventDispatcher\Event;
 2 use Acme\StoreBundle\Event\StoreSubscriber;
 3
 4 class Foo
 5 {
 6     private $started = false;
 7
 8     public function myLazyListener(Event $event)
 9     {
10         if(false === $this->started){
11             $subscriber = new StoreSubscriber();
12             $event->getDispatcher()->addSubscriber($subscriber);
13         }
14         $this->started = true;
15
16         //...更多代码
17     }
18 }

从一个监听者内部派遣另外的事件:

 1 use Symfony\Component\EventDispatcher\Event;
 2
 3 class Foo
 4 {
 5     public function myFooListener(Event $event)
 6     {
 7         $event->getDispatcher()->dispatch(‘log‘,$event);
 8
 9         //... 更多代码
10     }
11 }

如果你的应用程序中使用多个EventDispatcher实例,你可能需要专门注入一个已知EventDispatcher实例到你的监听器。这可以通过构造函数或者setter方法注入:

 1 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 2
 3 class Foo
 4 {
 5     protected $dispatcher = null;
 6
 7     public function __construct(EventDispatcherInterface $dispatcher)
 8     {
 9         $this->dispatcher = $dispatcher;
10     }
11 }

setter方法注入:

 1 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 2
 3 class Foo
 4 {
 5     protected $dispatcher = null;
 6
 7     public function setEventDispatcher(EventDispatcherInterface     $dispatcher)
 8     {
 9         $this->dispatcher = $dispatcher;
10     }
11 }

以上两种注入方法选用哪一个完全取决于个人喜好。一些人倾向于构造器注入,因为在构造时就能够完全初始化。但是当你有一个很长的依赖名单时,使用setter注入就是个可选的方式,尤其是在依赖项是可选的情况下。

7.分配器的简写使用方式:
EventDispatcher::dispatch方法总是返回一个Event对象。这样就给我们提供了很多简写的机会。比如一个不需要自定义Event对象的事件,它完全可以依靠原生的Event对象来派遣,你不需要给dispatch方法传入任何Event对象,它自己会创建一个默认的Event对象来使用。

$dispatcher->dispatch(‘foo.event‘);

更深一步,EventDispatcher总是返回被派遣的事件对象,无论是传入的还是自己内部创建的。

这样我们就可以做一些美观的简写:

if(!$dispatcher->dispatch(‘foo.event‘)->isPropagationStopped()){
    //....
}

或者:

$barEvent = new BarEvent();
$bar = $dispatcher->dispatch(‘foo.event‘,$barEvent)->getBar();

又或者:

$response = $dispatcher->dispatch(‘bar.event‘, new BarEvent())->getBar();

8.事件名称的内部自知
因为EventDispatcher在分配事件过程中早已经知道了事件的名称,事件名称又是被注入到Event对象中,所以,对于事件监听者来说完全可以通过getName()方法获取它。

这样事件名称就可以(和其它在自定义Event中包含的其它数据一样)作为监听者处理事件流程的一部分使用了。

use Symfony\Component\EventDispatcher\Event;

class Foo
{
    public function myEventListener(Event $event)
    {
        echo $event->getName();
    }
}

9.其它类型事件分配器:
服务容器感知的事件分配器 ContainerAwareEventDispatcher 是一个比较特殊的事件分配器实现。它耦合了服务容器,作为依赖注入组件的一部分实现。它允许把服务作为指定事件的监听者,从而让事件分配器具备了极强的性能。

服务在容器中时延迟加载的,这就意味着作为监听者使用的服务只有在一个事件被派遣后需要这些监听者时才被创建。

安装配置比较简单只需要把一个ContainerInterface注入到ContainerAwareEventDispatcher即可:

1 use Symfony\Component\DependencyInjection\ContainerBuilder;
2 use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
3
4 $container = new ContainerBuilder();
5 $dispatcher = new ContainerAwareEventDispatcher($container);

添加监听者:
容器知道事件分配器既可以通过直接加载特定服务,也可通过实现EventSubscriberInterface接口的实现。

下面的示例假设服务勇气已经加载了一些出现的服务:
注意服务必须在容器中标注为public的。

添加服务:
使用addListenerService()方法来连接已存在的服务定义,这里的$callback变量是一个数组:

array($serviceId, $methodName)

$dispatcher->addListenerService($eventName,array(‘foo‘,‘LogListener‘));

添加订阅者服务:
可以通过addSubscriberService()方法添加EventSubscribers对象,这里第一个参数是订阅者服务ID,第二个参数是服务类的名称(该类必须实现了EventSubscriberInterface接口):

$dispatcher->addSubscriberService(
    ‘kernel.store_subscriber‘,
    ‘StoreSubscriber‘
);

EventSubscriberInterface具体实现:

 1 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 2 // ...
 3
 4 class StoreSubscriber implements EventSubscriberInterface
 5 {
 6     public static function getSubscribedEvents()
 7     {
 8         return array(
 9             ‘kernel.response‘ => array(
10                 array(‘onKernelResponsePre‘, 10),
11                 array(‘onKernelResponsePost‘, 0),
12             ),
13             ‘store.order‘ => array(‘onStoreOrder‘, 0),
14         );
15     }
16
17     public function onKernelResponsePre(FilterResponseEvent $event)
18     {
19         // ...
20     }
21
22     public function onKernelResponsePost(FilterResponseEvent $event)
23     {
24         // ...
25     }
26
27     public function onStoreOrder(FilterOrderEvent $event)
28     {
29         // ...
30     }
31 }

10.还有一种事件分配器叫做不变事件分配器(Immutable Event Dispatcher):
它是一个固定的事件分配器。它不能注册新的监听者或者订阅者。它使用其它事件分配器注册的监听者或者订阅者。从这个角度说它只是一个原有事件
分配器的代理。
要使用它,首先需要创建一个标准的事件分配器(EventDispatcher 或者 ContainerAwareEventDispatcher)并为其注册一些监听者或者事件订阅者。

use Symfony\Component\EventDispatcher\EventDispatcher;

$dispatcher = new EventDispatcher();
$dispatcher->addListener(‘foo.action‘, function ($event) {
    // ...
});

// ...

然后将这个标准的事件分配器注入到一个ImmutableEventDispatcher中:

use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;
// ...

$immutableDispatcher = new ImmutableEventDispatcher($dispatcher);

那么从现在开始你就需要使用这个新的事件分配器了。

使用该代理事件分配器的好处是,如果你视图执行一个方法来修改该dispatcher(比如使用其addListener方法)将会收到一个 BadMethodCallException异常被抛出。

11.最后我们看一下通用的事件对象(Event Object)
在我们调用dispatcher的dispatch方法时如果不给其传入一个自定义的Event对象,那么Dispatcher会自动创建一个默认的Event对象。 这类的Event基类是由Event Dispatcher组件提供,是特意按照面向对象方式设计的API特定对象。它为复杂的应用程序提供了更加优雅可读性更强的代码。

而GenericEvent是一个方便用于那些希望在整个应用程序中都只使用一个事件对象的情况。它适合于大多数开箱即用的目标,因为它遵循了观察者模式,这种模式下事件对象封装了一个事件主题"subject",以及一些额外的可选扩展参数。

GenericEvent除了其基类Event外还拥有一个简洁的API:
__construct() 构造器可以接收事件主题和任何参数
getSubject() 获取主题
setArgument() 通过键设置一个参数
setArguments() 设置一个参数数组
getArgument() 通过键获取一个参数值
getArguments() 获取所有参数值
hasArgument() 如果某个键值存在,则返回true。

GenericEvent同时还在参数集上实现了ArrayAccess,所以可以非常方便的通过传入额外的参数。
下面是示例假设事件监听者已经被添加到dispatcher。

 1 use Symfony\Component\EventDispatcher\GenericEvent;
 2
 3 $event = new GenericEvent($subject);
 4 $dispatcher->dispatch(‘foo‘, $event);
 5
 6 class FooListener
 7 {
 8     public function handler(GenericEvent $event)
 9     {
10         if ($event->getSubject() instanceof Foo) {
11             // ...
12         }
13     }
14 }

通过ArrayAccess的API传入和处理事件参数:

 1 use Symfony\Component\EventDispatcher\GenericEvent;
 2
 3 $event = new GenericEvent(
 4     $subject,
 5     array(‘type‘ => ‘foo‘, ‘counter‘ => 0)
 6 );
 7 $dispatcher->dispatch(‘foo‘, $event);
 8
 9 echo $event[‘counter‘];
10
11 class FooListener
12 {
13     public function handler(GenericEvent $event)
14     {
15         if (isset($event[‘type‘]) && $event[‘type‘] === ‘foo‘) {
16             // ... do something
17         }
18
19         $event[‘counter‘]++;
20     }
21 }

过滤数据:

 1 use Symfony\Component\EventDispatcher\GenericEvent;
 2
 3 $event = new GenericEvent($subject, array(‘data‘ => ‘foo‘));
 4 $dispatcher->dispatch(‘foo‘, $event);
 5
 6 echo $event[‘data‘];
 7
 8 class FooListener
 9 {
10     public function filter(GenericEvent $event)
11     {
12         strtolower($event[‘data‘]);
13      }
14 }

我们可以在很多地方来直接使用这个GenericEvent对象。

原文链接:http://symfony.com/doc/current/components/event_dispatcher/introduction.html

Symfony2学习笔记之事件分配器

时间: 2024-10-29 03:27:35

Symfony2学习笔记之事件分配器的相关文章

Symfony2学习笔记之数据库操作

数据库和Doctrine让我们来面对这个对于任何应用程序来说最为普遍最具挑战性的任务,从数据库中读取和持久化数据信息.幸运的是,Symfony和Doctrine进行了集成,Doctrine类库全部目标就是给你一个强大的工具,让你的工作更加容易. Doctrine是完全解耦与Symfony的,所以并不一定要使用它. 一个简单例子:一个产品,我们首先来配置数据库,创建一个Product对象,持久化它到数据库并把它读回来. 首先我们需要创建一个bundle: $php app/console gene

Symfony2 学习笔记之内部构件

Symfony2内部是怎样工作的以及我们如何来扩展它呢?从外部整体上看,symfony2代码是由许多独立的层构成,每一层都是建立在前一层基础之上.其中,自动加载时不受框架直接管理的,它完全是在UniversalClassLoader类和src/autoload.php文件的帮助下独立完成的. HttpFoundation 组件最深层次的是HttpFoundation组件,它提供了处理HTTP所需的主要对象.是一个对一些PHP函数和变量的面向对象抽象.包括:Request 类,抽象了PHP中主要的

Symfony2学习笔记之HTTP Cache

富web应用程序的本质意味着它们的动态.无论你的应用程序多么有效率,每个请求比起静态文件来说总会存在很多的耗费.对于大多数web程序来说,这没什么. Symfony2非常的轻快,无论你做些严重超载的请求,每个请求将会得到很快的回复,而不会对你的服务器造成压力.但是随着你站点的成长,负载将成为一个严重的问题.对每个请求处理应该只被正常执行一次.这就是缓存真正要达成的目标. 站在巨人肩膀上的缓存:提高一个应用程序执行效率的最有效方法是缓存一个页面的所有输出然后让后续的请求绕开整个应用程序.当然,这对

Symfony2 学习笔记之插件格式

一个bundle类似于其它框架中的插件,但是比插件表现更好.它跟其它框架最主要的不同是在Symfony2中所有东西都是bundle,包括核心框架功能和你写的所有应用程序代码.Symfony2中,bundle可是一等公民.这给了你使用其它第三方开发的内容包或者分发你自己的bundle更多灵活性.你可以方便的选择哪些内容可以应用到你的程序中那些不用,来根据你的想法优化它们. 一个bundle就是一个目录,它具有很好的结构性,它能存放从类到controller和web资源等任何东西. 一个bundle

Caliburn.Micro学习笔记(三)----事件聚合IEventAggregator和 Ihandle<T>

Caliburn.Micro学习笔记(三)----事件聚合IEventAggregator和 Ihandle<T> 今天 说一下Caliburn.Micro的IEventAggregator和IHandle<T>分成两篇去讲这一篇写一个简单的例子 看一它的的实现和源码 下一篇用它们做一个多语言的demo 这两个是事件的订阅和广播,很强大,但用的时候要小心发生不必要的冲突. 先看一下它的实现思想 在Caliburn.Micro里EventAggregator要以单例的形式出现这样可以

学习笔记---Javascript事件Event、IE浏览器下的拖拽效果

学习笔记---Javascript事件Event.IE浏览器下的拖拽效果     1. 关于event常用属性有returnValue(是否允许事件处理继续进行, false为停止继续操作).srcElement(触发事件的事件源对象)和attachEvent("onclick",function(){...}); 2. a. 实现拖放(Drag and Drop): 目前支支持IE, 若定制某对象为可拖放对象, 则必须覆盖目标对象的dragenter和dragover事件, 可以用e

IOS学习笔记-触摸事件

一.事件传递的过程1.用户手指触摸屏幕,产生一个事件对象 2.系统会将这个事件对象添加到事件队列(先进先出)中 3.由UIApplication取出事件队列中的事件对象进行处理 4.UIApplication会先将事件对象传递给主要的UIWindow(当然,要先检测UIWindow是否能够接收和传递事件) 5.UIWindow会遍历所有的子控件,看看触摸点有没有落在某个子控件上面: 1> 如果触摸点没有落在子控件上,那么就由UIWindow直接处理这个事件,事件传递完毕 2> 如果触摸点落在子

Symfony2学习笔记之数据校验

校验在web应用程序中是一个常见的任务.数据输入到表单需要被校验.数据在被写入数据库之前或者传入一个webservice时也需要被校验. Symfony2 配备了一个Validator 组件,它让校验工作变得简单易懂.该组件是基于JSR303 Bean校验规范.一个Java规范用在PHP中. 基本验证理解校验的最好方法是看它的表现.首先,假设你已经创建了一个用于你应用程序某个地方的PHP对象. //src/Acme/BlogBundle/Entity/Author.php namespace A

Symfony2学习笔记之表单

对于一个Web开发者来说,处理HTML表单是一个最为普通又具挑战的任务.Symfony2集成了一个Form组件,让处理表单变的容易起来.在这一节里,我们将从基础开始创建一个复杂的表单,学习表单类库中最重要的内容. Symfony2 的Form组件是一个独立的类库,你可以在Symfony2项目之外使用它. 创建一个简单的表单:假设你要创建一个应用程序的todo列表,需要显示一些任务.因为你的用户需要编辑和创建任务,所以你需要创建一个表单.在你开始之前,首先来看通用的Task类,用来表示和存储一个单