是的,都说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 <achun.shx@qq.com> 格式
* 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 <achun.shx@qq.com>
* @copyright Copyright © 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;
}
}
分享到:
相关推荐
DuoLamPHP框架命名是按照多啦A梦动漫给改成的...作为一个PHPer,你还需要什么呢? 我们的口号是: 给力的超轻量级PHP框架。 原因: 用了的人都知道! DuoLamPHP框架功能概述: 单一入口模式 数据库使用PDO (确保开启
Tastphp:为现代化的 PHPer 准备的 PHP 框架
phper杂志第一期.rar
phper之路, 给学PHP初学者明路
杂志的内容完全由网上的PHPer们积极提供。在发行致辞中,《PHPer》创作组希望,以履行一个交流平台的使命来运作该杂志--“收集用户之所见,回馈用户之所求。”
来写一个PHP MVC框架开发的简明教程,首先声明,教程里面的框架不是一个完善的框架,只是一种思路,当然每个人对MVC框架实现的方法肯定是有差异的,希望高手多提意见多指正,和我一样的菜鸟多讨论多交流,刚接触MVC...
PHPChina推出的一本PHP开源技术性质的杂志,每两个月出一期。
phper新手?还在漫无目的地学习php?那就参考一下技能树吧
史上最难PHPer笔试题_40分就能月薪过万史上最难PHPer笔试题_40分就能月薪过万
国内的各种网店网城软件系统早已确立了各自面向的群体和...并介绍了商城设计原理、模板的制作、修改和使用、以及商城的优化等等技术,为广大爱好者和使用者深入详细的了解各个网店网城软件系统提供一个更加直观的窗口。
如果你访问上述网址中相关产品的官方网站,你将会注意到,每个论坛都有一个共同的热点,就是到底哪一个框架是最好的?事实似乎是每个都有它的长处,而且又都有自己的弱点。我的评估标准是:我很忙;因此框架应该节省...
作为一个PHPer,你还需要什么呢? 我们的口号是: 给力的超轻量级PHP框架。 原因: 用了的人都知道! DuoLamPHP框架功能概述: 单一入口模式 数据库使用PDO (确保开启了PDO,不然无法使用数据库) ...
CodeIgniter 介绍 大多数PHPer都想写出运行状态良好的应用程序,而且希望尽可能做得简单且不费事。这篇文章是有关 CodeIgniter的(以后简称CI),CI是一个达成上面目的的所谓框架。
大多数PHPer都想写出运行状态良好的应用程序,而且希望尽可能做得简单且不费事。这篇文章是有关 CodeIgniter的(以后简称CI),CI是一个达成上面目的的所谓框架。
phper1 phpchina
专门针对phper的杂志,糅合了许多成熟的开发理念
作为一位程序员,面试过多次,...PHPer 的开发的技术栈大多是 LAMP 或者是 LNMP,其中 Linux、Apache(Nginx)都比较偏运维,但是 PHP、MySQL 是每一位合格的 PHPer 都必须精通的技术栈。 而 Web 开发又不单单只靠 PHP
php手册chm,大家可以参考一下,应该有用的
phper 程序员最新一期杂志,讲到flex
作为一个PHPer,你还需要什么呢? 我们的口号是: 给力的超轻量级PHP框架。 原因: 用了的人都知道! DuoLamPHP框架功能概述: 单一入口模式 数据库使用PDO (确保开启了PDO,不然无法使用数据库) 核心编译功能,...