每一个PHPer都应该尝试写个框架,规划一个试试

是的,都说PHP写框架门槛低,我觉得这是个好事,框架的目的是更高效率的开发,门槛低为高效率提供了更多基础。有这么好的基础没有理由不自己也尝试写个框架,这里整理下自己要写框架的规划。


基于PHP5.3.X
这个都出来几年了,一些新特性的确能提高代码效率。

 

CLASS不是全部

OOP编程方式是非常可取的,一说OOP,直接对应的就是CLASS化,但是我们还需要独立的函数,PHP有命名空间,这些函数可以用命名空间包装起来,达到包化


CLASS风格封装配置和常量定义

虽然命名空间隔离了代码,但是隔离不了常量定义,比如

 

define('DEBUG',1);

又或者是一个全局的变量,个人认为作为一个框架不应该暴露出任何一个全局变量和常量定义。原因,如果应用项目够庞大,这种暴露无疑留下了冲突的危险

控制生命周期

一般认为PHP代码没有长声明周期的,这是常见的PHP加载方式造成的,可并不等于说就不需要考虑代码执行期间生成对象的生命周期了,要知道PHP也可以开发独立的服务器,具有长生命周期的代码。因此把需要具有长生命周期的对象用CLASS单件模式封装,其他变量都在某个入口函数中完成达到对象生命周期的控制

 

平面化CLASS
OOP的方式下,常常会看到CLASS一重有一重的继承,这样的好处就不说了,不过这让维护确实麻烦了,甚至好多基础类根本就是个空CLASS,好像仅仅是为了说明继承关系而产生的,为了OO而OO的代码。事实上我常常感觉通过CLASS继承并不能简单的通过维护基础类来解决新需求。所以平面化CLASS,减少CLASS继承的深度反而让事情会变得简单写,那要控制到多少深度呢?3层,不能超过这个数了。古语讲事不过三,这是人类社会早就总结出的哲学。原因道理不必深究,人家早就研究过了。

允许同一类别的CLASS具有不一致性接口
比如说数据库类操作,往往我们希望对数据库类操作提供完全一致性的接口,事实上由于数据库引擎的不同,差异性总是存在的,
不值得为了这些差异性浪费脑细胞达到接口一致。20/80法则,有必要允许一些不一致性来节省生命,别担心应用层使用的麻烦,实际中这些差异性总是确定的。数据转移怎么办?哦,别扯这个了,数据库真的转移了,要改的东东那大了去了,就别计较这一点儿了。


同类函数具有相同的参数个数
举个例子来说明这个想法,数据有效性验证,用3验证来说明这个问题。
这是3个关于最小值,最大值有效性数据的验证

 

function min($val,$min){...}
function max($val,$max){...}
function range($val,$min,$max){...}

 

我要说什么呢?我要说的是参数的写法,我认为这样更好

 

function min($val,array $args){...}
function max($val,array $args){...}
function range($val,array $args){...}

$args是个Key-Value的数组,这样通过数据结构传递参数,达到验证类函数的参数都是2个,调用接口的一致性利于逻辑化代码。

好像还有些,先写到这里。呵呵,不能纸上谈兵,贴上一段输入验证的代码,我管这个叫过滤器

 

 

<?php
namespace JingYesFilter;
use JingYes as J;
/**
 * 数据验证过滤配置,消息模板和一些常量设置
 * 真正的验证过程是通过独立的函数完成的
 */
final class Config
{
	public static $conf=array(
		/**
		 * 常量,这些值将在具体的规则函数里面使用
		 */
		'constant'=>array(
			'emailPattern'=>'/^[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/',
			'emailFullPattern'=>'/^[^@]*<[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?>$/'
		),
		/**
		 * 消息默认设置
		 */
		'messageType'=>'无效参数',
		'messageDefault'=>'未能通过"$_rule"规则',
		/**
		 * 消息模板,当规则未通过的时候被使用
		 */
		'message'=>array(
			'notEmpty'=>'非空',
			'isEmpty'=>'必须空',
			'enum'=>'取值之一',
			'min'=>'最小值',
			'max'=>'最大值',
			'range'=>'值范围($min,$max)',
			'length'=>'长度($min,$max)',
			'email'=>'电子邮件格式',
			'type'=>'要求类型"$type"',
			'date'=>'日期',
			'url'=>'网址'
		)
	);
	public static function extend($key,$val){
		if(is_array(self::$conf[$key]) and is_array($val))
			self::$conf[$key]=array_merge(self::$conf[$key],$val);
		else
			self::$conf[$key]=$val;
	}
}
/**
 * 带错误信息的过滤器代理
 * 通过这是函数可以访问具体的规则器
 * @param mixed $value,需要过滤的数据
 * @param array $rules,过滤规则
 *	rules 是Key-Value的数组,Value也是Key-Value数组
 *	例:array('min'=>array('min'=1))
 *	特殊规则 allowNull 如果被定义将优先执行
 * @return
			null	成功不返回任何值
			string 失败返回错误提示信息
 */
function proxy(&$value,array $rules)
{
	if(!$rules or $rules['allowNull'] and is_null($value)) return;
	foreach($rules as $rule=>$args) {
		if($rule=='allowNull') continue;
		$func=__NAMESPACE__.'\\'.$rule;
		if(!function_exists($func))
			J\ThrowException('未实现此功能',$rule,415,array('rule'=>$rule));
		if(is_scalar($args))
			$args=array($rule=>$args);
		$msg=$func(&$value,$args);
		if(true===$msg or null===$msg) continue;
		if(false===$msg)
			$msg=Config::$conf['message'][$rule]?:Config::$conf['messageDefault'];
		else
			$msg=Config::$conf['message'][$rule].$msg;
		$args['_value']=is_scalar($value)?$value:'value';
		$args['_rule']=$rule;
		J\ThrowException(Config::$conf['messageType'],$msg,415,$args);
	}
}
/**
 * 范围检查
 * 支持string数据类型和numeric类型
 * @note $args[min] <= val >= $args[max]
 * @param mixed $val 等待验证的值
 * @param array $args array(min=>foo,max=>foo)
 */
function range($val,array $args)
{
	if(is_numeric($args['min']))
		return is_numeric($val)
		and $val>=$args['min']
		and $val<=$args['max'];
	elseif(is_string($args['min']))
		return is_string($val)
		and $val>=$args['min']
		and $val<=$args['max'];
	return false;
}
/**
 * numeric 类型的值范围规则
 */
function min($val,array $args)
{
	if(is_numeric($args['min']))
		return is_numeric($val)
		and $val>=$args['min'];
	elseif(is_string($args['min']))
		return is_string($val)
		and $val>=$args['min'];
	return false;
}
function max($val,array $args)
{
	if(is_numeric($args['min']))
		return is_numeric($val)
		and $val<=$args['max'];
	elseif(is_string($args['min']))
		return is_string($val)
		and $val<=$args['max'];
	return false;
}
function enum($val,array $args=array('enum'=>array()))
{
	if(is_scalar($val) and in_array($val,$args['enum'])) return;
	return '('.implode($args['enum'],',').')';
}
function isEmpty($val)
{
	return empty($val);
}
function notEmpty(&$val)
{
	return !empty($val);
}
function length($val,array $args)
{
	if(!is_scalar($val) or !isset($args['min']) and !isset($args['max']))
		return false;
	$len=mb_strlen($val);
	if(isset($args['min']) and $len<$args['min'])
		return false;
	if(isset($args['max']) and $len>$args['max'])
		return false;
}
/**
 * 电子邮件格式校验
 * @param array agrs,可以包含选项Key
 *	allowName:允许类似 Yu HengChun <[email protected]> 格式
 *	checkMX:检验邮件服务器有效性
 */
function email($val,$args){
	$emailPattern=Config::$conf['constant']['emailPattern'];
	$emailFullPattern=Config::$conf['constant']['emailFullPattern'];
	$valid=is_string($val) && (preg_match($emailPattern,$val) || $args and $args['allowName'] and preg_match($emailFullPattern,$val));
	if($valid and $args and $args['checkMX']){
		$domain=rtrim(substr($val,strpos($val,'@')+1),'>');
		if(function_exists('checkdnsrr'))
			$valid=checkdnsrr($domain,'MX');
		if($valid && function_exists('fsockopen')){
			$valid=fsockopen($domain,25);
			if($valid){
				fclose($valid);
				$valid=true;
			}
		}
	}
	return $valid;
}
/**
 * 日期,可对数据进行有效性验证和数据处理
 * @param array $args 数据处理
 *	format:参见 PHP::date 的格式
 *  timezone:原数据的时区
 *  totimezone:转换到时区时间
 */
function date(&$val,array $args=array()) {
	if(!is_scalar($val)) return false;
	try {
		if($args['timezone'])
			$d=new \DateTime($val,new \DateTimeZone($args['timezone']));
		else
			$d=new \DateTime($val);
		if($args['totimezone'])
			$d->setTimezone(new \DateTimeZone($args['totimezone']));
		if($args['format']) {
			$val=$d->format($args['format']);
		}else {
			$val=$d->format('Y-m-d H:i:s');
		}
	}catch(Exception $e){
			return false;
	}
}
/**
 * 网址,支持scheme,host,path正则匹配验证
 * @note 不支持scheme,host,path有关联的匹配
 */
function url($val,array $args=array()) {
	$a=parse_url($val);
	if(!$a) return false;
	if(isset($args['scheme']) and is_scalar($args['scheme']))
		return 'args[scheme] $scheme error';
	if(is_array($args['scheme']) and !in_array($a['scheme'],$args['scheme']))
		return 'scheme must be in ('.implode($args['scheme'],',').')';
	if(isset($args['host']) and is_scalar($args['host']))
		return 'args[host] $host error';
	if(is_array($args['host'])){
		if(!$a['host']) return 'host must be in ('.implode($args['host'],',').')';
		$find=false;
		$h=$a['host'];
		foreach($args['host'] as $reg) {
			if(1==preg_match($reg,$h)){
				$find=true;
				break;
			}
		}
		if(!$find) return 'host must be in ('.implode($args['host'],',').')';
	}
	if(isset($args['path']) and is_scalar($args['path']))
		return 'args[path] $path error';
	if(is_array($args['path'])){
		if(!$a['path']) return 'path must be in ('.implode($args['path'],',').')';
		$find=false;
		$h=$a['path'];
		foreach($args['path'] as $reg) {
			if(1==preg_match($reg,$h)){
				$find=true;
				break;
			}
		}
		if(!$find) return 'path must be in ('.implode($args['path'],',').')';
	}
}

 

也许你注意到了Config这个类,整个过滤器的组织方式使用命名空间封装独立函数,class Config封装配置和文本消息(这样可以方便的用于多语言翻译),所有过滤器具有一致的2参数接口,那个代理proxy函数是为了方便调用制作的。
被使用的JingYes.php

 

<?php
/**
 * JingYes 框架使用的基本函数
 * 使用者可以预先定义这些函数
 * @author Yu HengChun <[email protected]>
 * @copyright Copyright &copy; 2011 Yu HengChun
 * @license http://www.opensource.org/licenses/bsd-license.php
 */
namespace JingYes;
/**
 * JingYes class风格配置
 * 其它类里面也采用这种风格
 */
final class Config
{
	public static $conf=array(
	);
	/**
	 * 配置扩展
	 * @param string $key 键
	 * @param array  $val 值
	 */
	public static function extend($key,$val){
		if(is_array(self::$conf[$key]) and is_array($val))
			self::$conf[$key]=array_merge(self::$conf[$key],$val);
		else
			self::$conf[$key]=$val;
	}
}
if(!function_exists('JingYes\ThrowException')){
/**
 * 抛出异常信息
 * @param string $type 错误类型信息
 * @param string $message 错误细节信息模板
 * @param integer $code 错误类型代码,采用http错误代码
 * @param array $args 传递给 $message的参数,用于生成最终信息
 */
function ThrowException($type,$message,$code=501,array $args=array()) {
	$type=Config::$conf['error'][$type]?:$type;
	$message=message($message,$args);
	throw new \Exception($type.':'.$message,$code);
}
}
if(!function_exists('JingYes\message')){
/**
 * 对提示信息进行转换翻译
 * @param string $message 错误细节信息模板
 * @param array $args 传递给 $message的参数,用于生成最终信息
 */
function message($message,array $args=array()) {
	$message=stripslashes($message);
	$message="\$message=\"$message\";";
	extract($args);
	eval($message);
	return $message;
}
}

你可能感兴趣的:(PHP,框架,JingYes)