博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
PHP 事件驱动框架 实践
阅读量:4978 次
发布时间:2019-06-12

本文共 13273 字,大约阅读时间需要 44 分钟。

关于PHP事件驱动框架的一些基本信息请先看我上一篇博客 

这里将使用上一篇博客中的写的事件类和规范来写一个简单  用php 和 mysql 模拟文件系统的增删改查  例子。

 

步骤


1.数据库设计和基本结构

  1.1我们用一张表来表示文件和文件夹,主要字段和意义如下表所示。

id 文件的唯一标识id  
name 文件名
uid 文件创建者的id
created 文件创建的时间戳
modified 文件修改的时间戳
size     文件大小
type     文件类型
pid   文件父目录的id
path 文件的物理位置
route 文件从父目录到根节点的所有祖先id  
version 文件版本号

    文件系统是一种简单的树形结构,特点是:文件夹节点有子节点,文件节点没有子节点。

 

2.类设计和定义

  2.1要考虑的问题

    从要实现的操作“增、删、改、查”来看,要考虑的主要问题只有一个,就是在对文件夹节点进行删改操作时,如何让上层目录和下层文件或文件夹进行相应的更改。

  2.2希望的写法

    1.对文件类(也可以用来表示文件夹)有简单的save , set , delete 等方法。

    2.希望有一个类来表示文件的所有祖先节点,这个类可以是一个简单的文件类的集合。它在必要时可以将所有的祖先文件夹实例化。它能提供一些简单的集合操作方法。

    3.希望有一个类来表示文件的所有子文件和子文件夹,他能提供一些方法让我批量操作子文件或文件夹。

 

  2.3类定义

    这里的实体类,和实体集合类参考了前端框架backbone。

    我先定义了一个实体类,这个类有基本增删改查操作。

    

class Entity {    private $attributes = array();    private $is_deleted = false;    public function __construct() {    }    public function reset( $data ) {        $this -> chunk();        return $this -> set( $data );    }    public function get( $attr_name ) {        return isset( $this -> attributes[$attr_name] ) ? $this -> attributes[$attr_name]->value : false;    }        public function get_attr_detail( $attr_name ){        return isset( $this -> attributes[$attr_name] ) ? $this -> attributes[$attr_name] : false;    }    public function set() {        if( $this -> is_deleted ){            return false;        }                $args = func_get_args();        if( count( $args) == 1 ){            $attr_array = ( array ) $args[0];        }else if(  count( $args) == 2 ){            $attr_array = array($args[0] => $args[1] );        }        foreach( $attr_array as $key => $value ) {            $value_obj = new stdClass();            $value_obj ->  changed = false;            $value_obj ->  new = true;                        if( isset($this -> attributes[$key])){                $value_obj -> new  = false;                if( $this -> attributes[$key] -> value !== $value ){                    $value_obj -> changed  = true;                    $value_obj -> last  = $this -> attributes[$key] -> value;                }            }            $value_obj -> value = $value;                        $this -> attributes[$key] = $value_obj;        }        return $this;    }    public function chunk() {        $this -> attributes = array( );        return $this;    }    public function save() {        return false;    }        public function delete(){        $this -> deleted = true;    }        public function to_object() {        $object = new stdClass();        foreach( $this -> attributes as $key => $value_obj ) {            $object -> $key = $value_obj ->value;        }        return $object;    }    public function to_array() {        return ( array ) $this -> to_object();    }        public function is_empty(){        return empty( $this -> attributes );    }        public function is_delete(){        return $this -> deleted;    }}

 

    然后定义了一个集合类,这个类实现了spl的IteratorAggregate 接口,使得php可以对它进行foreach操作。

class Entity_collection implements IteratorAggregate{    public $length = 0;    public $options = array(        'child_class' => "Entity"    );    public $children = array( );    public function __construct( $options ) {                $this -> options = array_merge( $this -> options, $options );            $this -> load_children( $this -> options );    }        private function load_children( &$options ){        if( !empty( $options['children_data'] ) ) {            $this -> reset_from_data( $options['children_data'] );                    } else if( !empty( $options['children'] ) ) {                        $this -> reset( $options['children'] );        }    }    public function to_array() {        $output = array( );        foreach( $this -> children as $child ) {            $output[] = $child -> to_object();        }        return $output;    }    public function reset( &$children ) {        $this -> children = $children;        $this -> length = count( $children );    }    public function reset_from_data( $children_data ) {        foreach( $children_data as $child_data ) {            $class_name = $this -> options['child_class'];            $children_obj = new $class_name( $child_data );            if( !$children_obj -> is_empty() ){                $this -> children[] = $children_obj;            }        }                $this -> length = count( $this -> children );    }        public function get( $id ){        foreach( $this -> children as &$child ){            if( $child -> get("id") == $id ){                return $child;            }        }        return false;    }        public function getIterator(){        return new ArrayIterator( $this -> children );    }}

  定义了文件(文件夹)类,继承自实体类。这里注意里面使用另一个叫做file_database的单例,这个单例是用来进行具体数据库操作。单独提出来是为了提高扩展性。用户可以通过修改file_database类来应对自己的需求。

class File extends Entity{    private $file_database;    public function __construct( $fid = false ){        parent::__construct();        $this -> file_database = File_database::get_instance();        if( $fid ){            if( is_object( $fid ) || is_array( $fid ) ){                $data = $fid;            }else{                $data = $this -> file_database -> load_file( $fid );            }            $this -> reset( $data );        }    }    public function save(){        $data = $this -> to_object();        $id = $this -> get( 'id' );        if( $id ){            $new_data = $this -> file_database -> update_file( $data );        }else{            $new_data = $this -> file_database -> insert_file( $data );        }        if( $new_data ){            $this -> reset( $new_data );        }else{            //错误处理        }        return $this;    }        public function delete(){            }}

  文件祖先类,这里注意它不是直接集成自实体集合类。

class File_ancenstors implements IteratorAggregate{    public $file = false;    public $ancestors;    public function __construct( $fid ){        $this -> file = new File( $fid );        $this -> load_ancestor_entities( $this -> file -> get( 'route' ) );    }    private function load_ancestor_entities( $route ){        $ancestor_ids = explode( '/', $route );        $collection_options = array(            'child_class' => "File",            'children_data' => $ancestor_ids        );        $this -> ancestors = new Entity_collection( $collection_options );        return $this;    }        public function getIterator(){        return $this -> ancestors;    }}

    子文件(不包括后代文件)集合类

class File_children extends Entity_collection{    private $file_database;        public function __construct( $fid ){        $this -> file_database = File_database::get_instance();        $options = array(            'child_class' => 'File',        );        parent::__construct( $options );                $this -> load_children_entities( $fid );    }    public function load_children_entities( $fid ){        $children_data = $this -> file_database -> load_children_files( $fid );        $this -> reset_from_data( $children_data  );        return $this;    }}

  最后数据库操作类

class File_database{    const table = 'files';    static $instance = false;    private $CI = false;    static function get_instance(){        if( !File_database::$instance ){            new File_database();                    }        return File_database::$instance;    }    private $db;    public function __construct(){        $this -> CI = &get_instance();        $this -> db = $this -> CI -> db;                self::$instance = &$this;    }    public function load_file( $fid ){        $mid = $this -> CI -> user -> id % var_get("user_mod");        $table_name = self::table ."_{
$mid}"; return $this -> db -> get_where( $table_name, array( "id" => $fid ) ) -> row(); } public function update_file( $file ){ if( !isset( $file -> id ) ){ return false; } $update_data = ( array ) $file; $mid = $update_data['uid'] % var_get("user_mod"); $table_name = self::table."_{
$mid}"; $this -> db -> where( "id", $update_data['id'] ); unset( $update_data['id'] ); $update_result = $this -> db -> update( $table_name, $update_data ); return $update_result ? $file : false; } public function insert_file( $file ){ if( isset( $file -> id ) ){ return false; } $insert_data = ( array ) $file; $insert_data['id'] = $this -> generate_file_id(); $mid = $insert_data['uid'] % var_get("user_mod"); $table_name = self::table."_{
$mid}"; $insert_result = $this -> db -> insert( $table_name, $insert_data ); if( $insert_result ){ $this -> db -> where( 'id', $insert_data['id'] ); $new_data = $this -> db -> get( $table_name ) -> row(); } return $insert_result ? $new_data : false; } private function generate_file_id(){ do{ $fid = $this -> salt( 16 ); }while( $this -> file_id_exist( $fid ) ); return $fid; } private function salt( $length = 16 ){ return substr( md5( uniqid( rand(), true ) ), 0, $length ); } public function file_id_exist( $fid ){ if( $this-> db -> query( "SELECT id FROM files WHERE id = '{
$fid}'" ) -> num_rows() ){ return true; } return false; } public function load_children_files( $fid ){ $mid = $this -> CI -> user -> id % var_get("user_mod"); $table_name = self::table ."_{
$mid}"; $this -> db -> where( 'pid', $fid ); $mysql_result = $this -> db -> get( $table_name ); return $mysql_result ? $mysql_result -> result() : array(); }}

 

  

 

3.模块定义

  3.1希望的写法

 

    1.希望在写对任何一个节点的逻辑操作时,无论增删改查,我都可以先只考虑考虑当前节点。通过抛出事件让自己的模块,或者其他扩展模块来决定要不要进行相应的其他操作。

 

    2.在写任何一个针对文件某一变化(如修改、删除)时,代码不关心只对自己模块内的变化负责。

  3.2模块实现

    这里我使用CodeIgniter作为基础框架,读者也可以直接自己移植。具体的内容请看注释。

load -> library("File_factory"); } //声明本模块需要进行权限验证的接口 public function auth(){ return array( 'main/file_list' => array( 'file_owner_validate' => array( 'validate' => 'is_operator_file_owner' ) ), 'main/file_update' => array( 'file_owner_validate' => array( 'validate' => 'is_operator_file_owner' ) ) ); } //声明模块要监听的事件,可以是内部事件,也可以是外部事件。 //这里监听的都是内部的文件操作的事件,如果修改,增加。为的是分离祖先后者后台的连带操作。 public function listen(){ return array( "file_update" => "react_file_update", "file_insert" => "react_file_insert", "file_remove" => "react_file_remove", ); } //文件更新后 更新父目录的size等属性。 public function react_file_update( $file ){ //size if( $file -> get_attr_detail("size") -> changed ){ $size_change_value = $file -> get("size") - $file -> get_attr_detail("size") -> last; $ancestors = new File_ancenstors( $file->get("id") ); foreach( $ancestors as &$ancestor ){ $ancestor -> set( "size", $ancestor -> get("size") + $size_change_value ); $ancestor -> save(); } } } //文件插入后,更新文件route。当然这个操作你可以在文件新建的时候就写好,这里只是示例。 public function react_file_insert( $file ){ $pfile = new File( $file -> get("pid") ); $file_route = explode('/', $pfile -> get('route') ); array_push( $file_route, $file -> get("pid") ); $file_route_str = implode('/', array_filter( $file_route) ); $file -> set("route" , $file_route_str) -> save(); } public function is_operator_file_owner(){ //根据自己情况进行判断 return true; } //文件的一个列表 public function file_list( $fid ){ $files = new File_children( $fid ); return $files -> to_array(); } //单个文件载入 public function file_load($fid){ $file = new File( $fid ); return $file -> to_object(); } //某一文件的祖先 public function file_ancestors( $fid ){ $file_ancenstors = new File_ancenstors( $fid ); return $file_ancenstors -> ancestors -> to_array() ; } //创建文件 public function file_new(){ //测试例子 $file_attrs = array( 'name' => "new_test", 'uid' => 151, 'created' => now(), 'pid' => '8a0341838c007cf4', ); $file = new File( $file_attrs ); $file -> save(); $this -> event -> trigger("file_insert", $file ); return $file; } //创建文件夹 public function folder_new( $pid, $name ){ $file_attrs = array( 'name' => $name, 'uid' => $this -> user -> id, 'created' => now(), 'pid' => $pid, ); $file = new File( $file_attrs ); $file -> save(); $this -> event -> trigger("file_insert", $file ); return $file -> to_object() ; } //文件更新 public function file_update( $fid, $data ){ $file = new File( $fid ); $file -> set( $data ); $file -> save(); $this -> event -> trigger( "file_update", $file ); }}?>

 

总结


事件驱动  相比 模块间通过接口来沟通 的最大好处应该是进一步降低了耦合,同时使系统的逻辑更容易阅读。

 

希望读者能够仔细阅读代码,能给我提出一些您的宝贵建议。毕竟我不希望我的博客只是放出一个现成的东西,编程让大家测试一下而已。贵在交流。

我有两个项目会基于事件驱动来做,非商业的,开源的,个人觉得还比较好玩。有兴趣参与的请与我联系。留言或者email都可以。谢谢。

 

    

 

转载于:https://www.cnblogs.com/sskyy/archive/2012/07/03/2574364.html

你可能感兴趣的文章
西电网络攻防大赛一个简单的上传绕过
查看>>
20145201 《信息安全系统设计基础》第14周学习总结
查看>>
【UNIX】select、poll、epoll学习
查看>>
sql 查看数据库环境及一些参数
查看>>
如何找出两个数组的相同元素?如果是多维数组呢?值类型除了基本类型还有引用类型呢?...
查看>>
江城子-苏轼
查看>>
Flask Web学习笔记(六)
查看>>
java 除法向上,向下取整
查看>>
Servlet的监听器
查看>>
c++中创建二维数组的几种方法
查看>>
python socket 学习
查看>>
软件开发冲刺2
查看>>
u-boot分析与移植——基于u-boot-2011.3和FL2440
查看>>
如何使得控件不需要在操作UI时检查InvokeRequired
查看>>
sql 中实现某个表中某字段拼接到一起
查看>>
OSX10.10 Yosemite安装Metasploit
查看>>
MyBatis知多少(4)MyBatis的优势
查看>>
添加Service Reference, 无法为服务生成代码错误的解决办法
查看>>
C语言基础(17)-作用域
查看>>
服务器常用词汇表
查看>>