<?php /** * Created by PhpStorm. * User: Administrator * Date: 2015/2/7 * Time: 11:22 */ trait Singletion { private static $instance; public static function instance() { self::$instance or self::$instance = new self; return self::$instance; } } trait MethodAble { private $methods; public function __construct() { $this->methods = new arrayObject; } public function setMethod( $handler, Closure $closure ) { if ( method_exists( $this, $handler ) || $this->methods->offsetExists( $handler ) ) { throw new Exception( "Cannot redeclare " . __CLASS__ . '::' . $handler, 1 ); } $this->methods->offsetSet( $handler, $closure ); return $this; } public function removeMethod( $handler ) { if ( $this->methods->offsetExists( $handler ) ) { $this->methods->offsetUnset( $handler ); } return $this; } public function __call( $method, $args = array() ) { if ( isset( $this->methods[ $method ] ) && is_a( $this->methods[ $method ], 'Closure' ) ) { return call_user_func_array( Closure::bind( $this->methods[ $method ], $this ), $args ); } return false; } public function getClosure( $handler ) { return isset( $this->methods[ $handler ] ) && is_a( $this->methods[ $handler ], 'Closure' ) ? $this->methods[ $handler ] : null; } } trait ServiceAble { private $service; public function __construct() { $this->service = ClassManager::make( 'Service', $this ); } public function service() { return $this->service; } public function once( $handler, Closure $closure, $priority = 0 ){ return $this->on( $handler, $closure, $priority, 1 ); } public function on( $handler, Closure $closure, $priority = 0, $once = 0 ) { $this->service->attack( $handler, is_a($this, 'Prototype') ? $this->container : $this, $closure, $priority, $once ); return $this; } public function off( $handler, $priority = null ) { $this->service->detack( $handler, $priority ); return $this; } public function trigger( $handler, $data = null ) { return $this->service->trigger( $handler, $data ); } } trait PrototypeAble { protected static $prototype; public function __construct() { static::$prototype = $this->prototype(); } public function prototype() { return static::$prototype ? static::$prototype : ClassManager::make( 'Prototype', $this ); } } class ClassManager { use Singletion; private $collections; private function __construct() { $this->collections or $this->collections = new ArrayObject(); } public static function make( $class ) { $collections = self::instance()->collections; if ( $collections->offsetExists( $class ) ) { $class = $collections->offsetGet( $class ); $args = func_get_args(); $argNum = func_num_args(); switch ( $argNum ) { case 2: return new $class( $args[1] ); break; case 3: return new $class( $args[1], $args[2] ); break; case 4: return new $class( $args[1], $args[2], $args[3] ); break; case 5: return new $class( $args[1], $args[2], $args[3], $args[4] ); break; case 6: return new $class( $args[1], $args[2], $args[3], $args[4], $args[5] ); break; default: return new $class(); break; } } return null; } public static function __callStatic( $func, $args ) { $collections = self::instance()->collections; if ( ! method_exists( $collections, $func ) ) { throw new Exception(); } return call_user_func_array( array( $collections, $func ), $args ); } } class Util { static function dump() { $dumps = func_get_args(); ob_start(); array_walk( $dumps, function ( $dump ) { var_dump( $dump ); } ); highlight_string( '<?php' . PHP_EOL . ob_get_clean() ); } } class Collection extends SplObjectStorage { } class Event { private $handler; private $target; private $closure; private $priority; private $data; private $once; private $index; public function __construct( $handler, $target, $closure, $priority = 0, $once = 0 ) { $this->handler = $handler; $this->target = $target; $this->closure = $closure; $this->priority = $priority; $this->once = $once; $this->index = null; } function setIndex( $index ) { $this->index = $index; return $this; } function setData( $data ) { $this->data = $data; return $this; } function __get( $key ) { return isset( $this->$key ) ? $this->$key : false; } } class Prototype { use MethodAble, ServiceAble { MethodAble::__construct as private __constructMethodAble; ServiceAble::__construct as private __constructServiceAble; } private $container; public function __construct( $container ) { $this->container = $container; $this->__constructMethodAble(); $this->__constructServiceAble(); } } class Service { private $eventCollections; private $component; public function __construct( $component ) { $this->component = $component; } public function attack( $handler, $component, Closure $closure, $priority = 0, $once = 0 ) { $priority = $priority < 0 ? 0 : $priority; $this->eventCollections[ $handler ][ $priority ][] = ClassManager::make( 'Event', $handler, $component, $closure, $priority, $once ); ksort( $this->eventCollections[ $handler ] ); } public function detack( $handler, $priority = null ) { if ( null == $priority ) { unset( $this->eventCollections[ $handler ] ); } else { unset( $this->eventCollections[ $handler ][ $priority ] ); } } public function trigger( $handler, $data = null ) { if ( isset( $this->component->prototype()->service()->eventCollections[ $handler ] ) ) { array_walk_recursive( $this->component->prototype()->service()->eventCollections[ $handler ], function ( $event, $key ) use ( $handler ) { $event->once and $event->setIndex($key); $this->eventCollections[ $handler ][ $event->priority ][] = $event; } ); } if ( isset( $this->eventCollections[ $handler ] ) ) { array_walk_recursive( $this->eventCollections[ $handler ], function ( $event, $key ) use ( $data, $handler ) { call_user_func( Closure::bind( $event->closure, $event->target ), $event->setData( $data ) ); if( $event->once ){ unset( $this->eventCollections[ $handler ][ $event->priority ][ $key ] ); if( $event->index !== null ){ unset( $this->component->prototype()->service()->eventCollections[ $handler ][ $event->priority ][ $event->index ] ); } } } ); } } } class Container { use MethodAble, ServiceAble, PrototypeAble { MethodAble::__construct as private __constructMethodAble; ServiceAble::__construct as private __constructServiceAble; PrototypeAble::__construct as private __constructPrototypeAble; } public function __construct() { $this->__constructMethodAble(); $this->__constructServiceAble(); $this->__constructPrototypeAble(); } public function __call( $method, $args = array() ) { if ( isset( $this->methods[ $method ] ) && is_a( $this->methods[ $method ], 'Closure' ) ) { return call_user_func_array( Closure::bind( $this->methods[ $method ], $this ), $args ); } if ( $closure = $this->prototype()->getClosure( $method ) ) { return call_user_func_array( Closure::bind( $closure, $this ), $args ); } return false; } } class Component extends Container { protected static $prototype; function doSome() { $this->service()->trigger( 'doSome' ); return $this; } } class Component2 extends Container { protected static $prototype; function doSome() { $this->service()->trigger( 'doSome' ); return $this; } } if ( version_compare(PHP_VERSION, '5.4', '<') ) { throw new Exception('minimum version PHP 5.4.0, current version: ' . PHP_VERSION , 1); } $classMap = array( 'Util', 'Collection', 'Prototype', 'Container', 'Event', 'Service', 'Component' ); array_walk( $classMap, function ( $class ) { ClassManager::offsetSet( $class, $class ); } ); $component = new Component; $component2 = new Component; $component->prototype()->setMethod( 'doSome4', function () { $this->trigger( 'doSome4', array( 'arg' ) ); } ); $component->setMethod( 'doSome2', function () { $this->trigger( 'doSome2' ); } ); $component2->setMethod( 'doSome3', function () { $this->trigger( 'doSome3' ); } ); $component->prototype()->once( 'doSome4', function ( $event ) { echo get_class($this) . '-'. $event->handler . '-' . $event->priority . '<br />'; }, 4 ); $component->prototype()->on( 'doSome4', function ( $event ) { echo get_class($this) . '-'. $event->handler . '-' . $event->priority . '<br />'; }, 3 ); $component->on( 'doSome', function ( $event ) { echo get_class($this) . '-'. $event->handler . '-' . $event->priority . '<br />'; }, 10 ); $component->on( 'doSome2', function ( $event ) { echo get_class($this) . '-'. $event->handler . '-' . $event->priority . '<br />'; }, 9 ); $component->on( 'doSome4', function ( $event ) { echo get_class($this) . '-'. $event->handler . '-' . $event->priority . '<br />'; }, 2 ); $component2->on( 'doSome', function ( $event ) { echo get_class($this) . '-'. $event->handler . '-' . $event->priority . '<br />'; }, 10 ); $component2->on( 'doSome3', function ( $event ) { echo get_class($this) . '-'. $event->handler . '-' . $event->priority . '<br />'; }, 11 ); $component2->on( 'doSome4', function ( $event ) { echo get_class($this) . '-'. $event->handler . '-' . $event->priority . '<br />'; }, 12 ); $component->removeMethod('doSome2'); $component->doSome(); $component->doSome2(); $component->doSome4(); echo '<hr />'; $component2->doSome(); $component2->doSome3(); $component2->doSome4();