综合实例

文件中写数据

简单写法,使用单例

<?php

// singleton & late static 

class fileDb {
    private $_filePath;
    private $_fp;
    protected static $_instance;

    public static function getInstance($filePath) {
        if(!isset(self::$_instance)) {
            self::$_instance = new static($filePath);
        }
        return self::$_instance;
    }

    private function __construct($filePath) {
        $this->_filePath = $filePath;
        $this->_fp = fopen($this->_filePath, 'a+');
    }

    public function __destruct() {
        fclose($this->_fp);
    }

    public function insert(array $row) {
        fputcsv($this->_fp, $row);
    }
}

$fileDb = fileDb::getInstance('./test.txt');
var_dump($fileDb);
$fileDb->insert(array('revin', '509129@qq.com'));

引入代码复用(Trait)

将单例模式提出来

禁止反序列化,

禁止克隆

<?php

// trait

trait Singleton {
    protected static $_instance;

    final public static function getInstance($config = array()) {
        if(!isset(self::$_instance)) {
            self::$_instance = new static($config);
        }
        return self::$_instance;
    }
    private function __construct($config) {
        $this->init($config);
    }
    protected function init() {}
    final private function __wakeup() {}
    final private function __clone() {}
}

class fileDb {
    use Singleton;
    private $_filePath;
    private $_fp;

    protected function init($config) {
        $this->_filePath = $config['filePath'];
        $this->_fp = fopen($this->_filePath, 'a+');
    }

    public function __destruct() {
        fclose($this->_fp);
    }

    public function insert(array $row) {
        fputcsv($this->_fp, $row);
    }
}

// step 1:

$fileDb = fileDb::getInstance(array('filePath' => './test.txt'));
$fileDb->insert(array('revin', '509129@qq.com'));

引入事件,闭包和回调函数

当除了插入操作还有其他附加操作,引入事件

<?php

// callback & closure & Event

trait Singleton {
protected static $_instance;

    final public static function getInstance($config = array()) {
        if(!isset(self::$_instance)) {
            self::$_instance = new static($config);
        }
        return self::$_instance;
    }
    private function __construct($config) {
        $this->init($config);
    }
    protected function init() {}
    final private function __wakeup() {}
    final private function __clone() {}
}

abstract class Component {
    protected $_events = [];

    public final function on($eventName, $handler) {
        if(!isset($this->_events[$eventName])) {
            $this->_events[$eventName] = [];
        }
        if(is_callable($handler)) {
            $this->_events[$eventName][] = $handler;
        }
    }

    public final function trigger($eventName, $data) {
        if(isset($this->_events[$eventName])) {
            foreach($this->_events[$eventName] as $handler) {
                call_user_func($handler, $data);
            }
        }
    }

    public function hasEventHandlers($eventName) {
        return !empty($this->_events[$eventName]);
    }
}

class FileDb extends Component {
    use Singleton;
    private $_filePath;
    private $_fp;

    protected function init($config) {
        $this->_filePath = $config['filePath'];
        $this->_fp = fopen($this->_filePath, 'a+');
    }

    public function __destruct() {
        fclose($this->_fp);
    }

    public function insert(array $row) {
        fputcsv($this->_fp, $row);

        if($this->hasEventHandlers('afterInsert')) {
            $this->trigger('afterInsert', $row);
        }
    }
}


$fileDb = fileDb::getInstance(array('filePath' => './test.txt'));

$fileDb->on('afterInsert', function($row) {
    echo "Inserted (From ".__FUNCTION__.")\n";
});
$fileDb->on('afterInsert', function($row) {
    echo "Inserted2 (From ".__FUNCTION__.")\n";
});

$fileDb->insert(array('revin', '509129@qq.com'));

引入行为机制(装饰模式)

引入行为模式:动态的向一些类加入一些比不存在的功能, 比如DbBehavior,并不存在于fileDb类中,但是我们又可以使用fileDb实例调用DbBehavior中的方法

<?php

// behavior & decorator 

trait Singleton {
    protected static $_instance;

    final public static function getInstance($config = array()) {
        if(!isset(self::$_instance)) {
            self::$_instance = new static($config);
        }
        return self::$_instance;
    }

    private function __construct($config) {
        $this->init($config);
    }

    protected function init($config) {}
    final private function __wakeup() {}
    final private function __clone() {}
}

class FileDb extends Component {
    use Singleton;

    private $_filePath;
    private $_fp;

    protected function init($config) {
        $this->_filePath = $config['filePath'];
        $this->_fp = fopen($this->_filePath, 'a+');
    }

    public function __destruct() {
        fclose($this->_fp);
    }

    public function getFilePointer() {
        return $this->_fp;
    }

    public function insert(array $row) {
        fputcsv($this->_fp, $row);

        if($this->hasEventHandlers('afterInsert')) {
            $this->trigger('afterInsert', $row);
        }
    }
}

abstract class Component {
    protected $_events = [];
    protected $_behaviors = [];

    public final function on($eventName, $handler) {
        if(!isset($this->_events[$eventName])) {
            $this->_events[$eventName] = [];
        }

        if(is_callable($handler)) {
            $this->_events[$eventName][] = $handler;
        }
    }

    public final function trigger($eventName, $data) {
        if(isset($this->_events[$eventName])) {
            foreach($this->_events[$eventName] as $handler) {
                call_user_func($handler, $data);
            }
        }
    }

    public function hasEventHandlers($eventName) {
        return !empty($this->_events[$eventName]);
    }

    public function attachBehavior($name, $behavior) {
        if($behavior instanceof Behavior) {
            $this->_behaviors[$name] = $behavior;
            $behavior->attach($this);
        }
    }

    public function __call($name, $params) {
        foreach($this->_behaviors as $behavior) {
            if(method_exists($behavior, $name)) {
                $method = new ReflectionMethod($behavior, $name);
                if($method->isPublic()) {
                    return call_user_func_array([$behavior, $name], $params);
                }
            }
        }
        throw new Exception('Unknown method: '.get_class($this)."::$name()");
    }
}

abstract class Behavior {
    public $owner;

    public function attach($to) {
        $this->owner = $to;
    }
}

class DbBehavior extends Behavior {

    public function getFirstRecord() {
        $fp = $this->owner->getFilePointer();
        $pos = ftell($fp);
        rewind($fp);
        $firstLine = fgets($fp);
        fseek($fp, $pos);
        return $firstLine;
    }
}

$fileDb = fileDb::getInstance(array('filePath' => './test.txt'));
$fileDb->on('afterInsert', function($row) {
    echo "Inserted (From ".__FUNCTION__.")\n";
});

$fileDb->insert(array('revin', '509129@qq.com'));
$fileDb->attachBehavior('DbBehavior', new DbBehavior());
echo "The First Line is: ".$fileDb->getFirstRecord();

进一步优化,将行为机制和事件机制结合

自动注册绑定事件回调函数

<?php

// behavior & decorator 

trait Singleton {
    protected static $_instance;

    final public static function getInstance($config = array()) {
        if(!isset(self::$_instance)) {
            self::$_instance = new static($config);
        }
        return self::$_instance;
    }

    private function __construct($config) {
        $this->init($config);
    }

    protected function init($config) {}
    final private function __wakeup() {}
    final private function __clone() {}
}

class FileDb extends Component {
    use Singleton;

    private $_filePath;
    private $_fp;

    protected function init($config) {
        $this->_filePath = $config['filePath'];
        $this->_fp = fopen($this->_filePath, 'a+');
    }

    public function __destruct() {
        fclose($this->_fp);
    }

    public function getFilePointer() {
        return $this->_fp;
    }

    public function insert(array $row) {
        fputcsv($this->_fp, $row);

        if($this->hasEventHandlers('afterInsert')) {
            $this->trigger('afterInsert', $row);
        }
    }
}

abstract class Component {
    protected $_events = [];
    protected $_behaviors = [];

    public final function on($eventName, $handler) {
        if(!isset($this->_events[$eventName])) {
            $this->_events[$eventName] = [];
        }

        if(is_callable($handler)) {
            $this->_events[$eventName][] = $handler;
        }
    }

    public final function trigger($eventName, $data) {
        if(isset($this->_events[$eventName])) {
            foreach($this->_events[$eventName] as $handler) {
                call_user_func($handler, $data);
            }
        }
    }

    public function hasEventHandlers($eventName) {
        return !empty($this->_events[$eventName]);
    }

    public function attachBehavior($name, $behavior) {
        if($behavior instanceof Behavior) {
            $this->_behaviors[$name] = $behavior;
            $behavior->attach($this);
        }
    }

    public function __call($name, $params) {
        foreach($this->_behaviors as $behavior) {
            if(method_exists($behavior, $name)) {
                $method = new ReflectionMethod($behavior, $name);
                if($method->isPublic()) {
                    return call_user_func_array([$behavior, $name], $params);
                }
            }
        }
        throw new Exception('Unknown method: '.get_class($this)."::$name()");
    }
}

abstract class Behavior {
    public $owner;

    public function attach($to) {
        $this->owner = $to;

        $methods = get_class_methods($this); 
        foreach($methods as $methodName) {
            if(strpos($methodName, 'on') === 0 && strlen($methodName) > 2) {
                $this->owner->on(lcfirst(substr($methodName, 2)), [$this, $methodName]);
            }
        }
    }
}

class DbBehavior extends Behavior {

    public function getFirstRecord() {
        $fp = $this->owner->getFilePointer();
        $pos = ftell($fp);
        rewind($fp);
        $firstLine = fgets($fp);
        fseek($fp, $pos);
        return $firstLine;
    }

    public function onAfterInsert($row) {
        echo "Inserted (From ".__CLASS__.")\n";
    }
}

$fileDb = fileDb::getInstance(array('filePath' => './test.txt'));
$fileDb->attachBehavior('DbBehavior', new DbBehavior());
$fileDb->insert(array('revin', '509129@qq.com'));
echo "The First Line is: " . $fileDb->getFirstRecord();

进一步引入命名空间,自动加载

文件结构

base目录
    ---Behavior.php
    ---Component.php
    ---Singleton.php
DbBehavior.php
FileDb.php    
index.php

base/Behavior.php

<?php
namespace app\base;

abstract class Behavior {
    public $owner;

    public function attach($to) {
        $this->owner = $to;

        $methods = get_class_methods($this);
        foreach($methods as $methodName) {
            if(strpos($methodName, 'on') === 0 && strlen($methodName) > 2) {
                $this->owner->on(lcfirst(substr($methodName, 2)), [$this, $methodName]);
            }
        }
    }
}

base/Component.php

<?php
namespace app\base;

include_once(APP_ROOT.DIRECTORY_SEPARATOR.'base'.DIRECTORY_SEPARATOR.'Behavior.php');

use app\base\Behavior;
use \ReflectionMethod;
abstract class Component {
    protected $_events = [];
     protected $_behaviors = [];

      public final function on($eventName, $handler) {
          if(!isset($this->_events[$eventName])) {
              $this->_events[$eventName] = [];
           } 
           if(is_callable($handler)) {
               $this->_events[$eventName][] = $handler; 
           } 
      } 

      public final function trigger($eventName, $data) {
          if(isset($this->_events[$eventName])) {
              foreach($this->_events[$eventName] as $handler) {
                  call_user_func($handler, $data); 
              } 
          } 
      } 

      public function hasEventHandlers($eventName) {
          return !empty($this->_events[$eventName]); 
      } 

      public function attachBehavior($name, $behavior) {
          if($behavior instanceof Behavior) {
              $this->_behaviors[$name] = $behavior; $behavior->attach($this); 
          } 
      } 

      public function __call($name, $params) {
          foreach($this->_behaviors as $behavior) {
              if(method_exists($behavior, $name)) {
                  $method = new ReflectionMethod($behavior, $name);
                   if($method->isPublic()) {
                       return call_user_func_array([$behavior, $name], $params); 
                   } 
              } 
          } 
          throw new Exception('Unknown method: '.get_class($this)."::$name()"); 
      }
}

base/Singleton.php.php

<?php
namespace app\base;

trait Singleton {
    protected static $_instance;

    final public static function getInstance($config) {
        if(!isset(self::$_instance)) {
            self::$_instance = new static($config);
        }

        return self::$_instance;
    }

    private function __construct($config) {
        $this->init($config);
    }

    protected function init() {}

    final private function __wakeup() {}
    final private function __clone() {}
}

DbBehavior.php

<?php
namespace app;

include_once(APP_ROOT.DIRECTORY_SEPARATOR.'base'.DIRECTORY_SEPARATOR.'Behavior.php');

use app\base\Behavior;

class DbBehavior extends Behavior {

    public function getFirstRecord() {
        $fp = $this->owner->getFilePointer();
        $pos = ftell($fp);
        rewind($fp);
        $firstLine = fgets($fp);
        fseek($fp, $pos);
        return $firstLine;
    }
    public function onAfterInsert($row) {
        echo "Inserted (From ".__CLASS__.")\n";
    }
}

FileDb.php

<?php
namespace app;

include_once(APP_ROOT.DIRECTORY_SEPARATOR.'base'.DIRECTORY_SEPARATOR.'Component.php');
include_once(APP_ROOT.DIRECTORY_SEPARATOR.'base'.DIRECTORY_SEPARATOR.'Singleton.php');

use app\base\Component;
use app\base\Singleton;

class FileDb extends Component {
    use Singleton;

    private $_filePath;
    private $_fp;

    protected function init($config) {
        $this->_filePath = $config['filePath'];
        $this->_fp = fopen($this->_filePath, 'a+');
    }

    public function __destruct() {
        fclose($this->_fp);
    }

    public function getFilePointer() {
        return $this->_fp;
    }

    public function insert(array $row) {
        fputcsv($this->_fp, $row);

        if($this->hasEventHandlers('afterInsert')) {
            $this->trigger('afterInsert', $row);
        }
    }
}

index.php

<?php
namespace app;

error_reporting(E_ALL);
ini_set('display_errors', true);

define('APP_ROOT', __DIR__);

include(APP_ROOT.'/FileDb.php');
include(APP_ROOT.'/DbBehavior.php');

$fileDb = fileDb::getInstance(array('filePath' => APP_ROOT .DIRECTORY_SEPARATOR. 'test.txt'));
$fileDb->on('afterInsert', function($row) {
    echo "Inserted (From ".__FUNCTION__.")\n";
});

$fileDb->attachBehavior('DbBehavior', new DbBehavior());
$fileDb->insert(array('revin', '509129@qqcom'));

echo "The First Line is: ".$fileDb->getFirstRecord();

进一步优化,使用自动加载机制

使用了自动加载机制,其它文件中的include删掉即可.

<?php
namespace app;

define('APP_ROOT', __DIR__);

function autoload($className) {
    if (strpos($className, 'app\\') === 0) {
        $className = str_replace('app\\', '', $className);
    }

    $classFile = APP_ROOT.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $className).'.php';
    include ($classFile);
}

spl_autoload_register('app\autoload'); //注册

try {
    $fileDb = fileDb::getInstance(array('filePath' => APP_ROOT .DIRECTORY_SEPARATOR. 'test.txt'));
} catch (Exception $e) {
    echo $e->getMessage();
}
$fileDb->on('afterInsert', function($row) {
    echo "Inserted (From ".__FUNCTION__.")\n";
});

$fileDb->attachBehavior('DbBehavior', new DbBehavior());
$fileDb->insert(array('revin', '509129@qqcom'));

echo "The First Line is: ".$fileDb->getFirstRecord();

Last updated