`
achun
  • 浏览: 314118 次
  • 性别: Icon_minigender_1
  • 来自: 河南郑州
社区版块
存档分类
最新评论

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

    博客分类:
  • PHP
阅读更多

是的,都说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 &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;
}
}
1
3
分享到:
评论
2 楼 we3ew 2011-08-25  
  
超级同意你的观点,我也挣着手写个框架并应用于马上要研发的项目。
1 楼 luolonghao 2011-07-04  
同意,每个程序员都应该写框架,写出来的框架不一定流行,但写框架的过程当中能获取大量知识,有利于理解现有的框架。

相关推荐

    DuckPhp 是一个 PHP 框架 回归 PHPer 的简单化

    一、教程 快速入门 ,快速入门页面。 文档索引页 ,所有文档索引页面,所有文档的集合入口 直接运行演示 cd template php ./duckphp-project run Composer 安装 composer require dvaknheo/duckphp # 用 require ./...

    phper杂志第一期.rar

    每一期的《PHPer杂志》都是PHP开发者提升技能、了解最新技术趋势的好帮手。通过阅读和学习,不仅可以拓宽知识视野,还能提升解决实际问题的能力。无论是新手还是有经验的开发者,都能从中受益。

    DuoLamPHP开源框架v1.0

    DuoLamPHP框架命名是按照多啦A梦动漫给改成的...作为一个PHPer,你还需要什么呢? 我们的口号是: 给力的超轻量级PHP框架。 原因: 用了的人都知道! DuoLamPHP框架功能概述: 单一入口模式 数据库使用PDO (确保开启

    phper11.rar

    在"phper11"这个单一的文件名中,我们无法得知具体的内容,但通常这样的命名可能表示这是一个系列教程或文章的一部分,比如可能是第11课、第11个案例研究,或者是关于PHP的第11个主题。 学习PHP,你需要掌握以下几...

    phper之路, 给学PHP初学者明路

    phper之路, 给学PHP初学者明路

    phper

    3. "爱书吧-2万本图书免费下载.url" - 这是一个链接,指向一个提供2万本免费图书下载的网站,可能是PHP和Web开发相关书籍的资源库,对Phper进行自我学习和提升大有裨益。 综合以上信息,我们可以得出,这是一份专为...

    一个基于go-zero开发、为PHP语言Laravel框架的PHPer们准备、支持GRPC的单体框架兼容go-zero框架

    Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。...如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

    PHP框架CXPHP.zip

    来写一个PHP MVC框架开发的简明教程,首先声明,教程里面的框架不是一个完善的框架,只是一种思路,当然每个人对MVC框架实现的方法肯定是有差异的,希望高手多提意见多指正,和我一样的菜鸟多讨论多交流,刚接触MVC...

    phper1

    在【压缩包子文件的文件名称列表】中,我们只有一个文件"phper1.pdf"。这很可能是一份详尽的PDF文档,包含了关于PHP开发的教程、指南或者是某个课程的讲义。通常,这样的文档会包含以下内容: 1. **PHP简介**:介绍...

    Tastphp为现代化的PHPer准备的PHP框架

    "Tastphp" 是一个专为现代化的 PHP 开发者设计的高效、灵活的 PHP 框架。这个框架旨在简化 PHP 应用程序的构建过程,提高开发效率,同时保持代码的可维护性和可扩展性。作为一款现代化的 PHP 开发框架,Tastphp 集成...

    PHP开源技术电子杂志《PHPer》

    《PHPer》杂志正是这样一个交流的载体,它将PHPer们的实践经验、技术心得、创新思路汇聚在一起,为读者提供了丰富的学习资源。 开源,是《PHPer》杂志的核心理念之一。开源意味着代码可以自由地查看、使用、修改和...

    phper技能树(图)

    phper新手?还在漫无目的地学习php?那就参考一下技能树吧

    史上最难PHPer笔试题_40分就能月薪过万

    史上最难PHPer笔试题_40分就能月薪过万史上最难PHPer笔试题_40分就能月薪过万

    《PHPer》开源杂志

    《PHPer》开源杂志是PHPChina倾力打造的一本专门针对PHP开源技术的期刊,每两个月发布一期,致力于为PHP程序员提供最新的技术资讯、实战经验以及行业动态。这份杂志不仅涵盖了PHP的基础知识,还深入探讨了PHP在各种...

    《PHPer》第八期

    国内的各种网店网城软件系统早已确立了各自面向的群体和...并介绍了商城设计原理、模板的制作、修改和使用、以及商城的优化等等技术,为广大爱好者和使用者深入详细的了解各个网店网城软件系统提供一个更加直观的窗口。

    phper 程序员最新一期杂志,讲到flex

    ActionScript是Flex的核心编程语言,它是基于ECMAScript的一个方言,具备面向对象特性,使得开发者能够创建复杂的动态应用。 在这一期的杂志中,我们可以期待深入探讨以下几点Flex与PHP的结合应用: 1. **Flex与...

    CodeIgniter:php敏捷开发框架web快速开发详解

    如果你访问上述网址中相关产品的官方网站,你将会注意到,每个论坛都有一个共同的热点,就是到底哪一个框架是最好的?事实似乎是每个都有它的长处,而且又都有自己的弱点。我的评估标准是:我很忙;因此框架应该节省...

    DuoLamPHP框架 v1.0

    作为一个PHPer,你还需要什么呢? 我们的口号是: 给力的超轻量级PHP框架。 原因: 用了的人都知道!   DuoLamPHP框架功能概述: 单一入口模式  数据库使用PDO (确保开启了PDO,不然无法使用数据库)  ...

    phper杂志20期免费下载

    在本期杂志中,我们可以从"PHPer20.pdf"这个文件名推测,它包含了完整的一期内容,包括但不限于技术文章、专家访谈、实战案例和行业趋势等板块。PDF格式确保了内容的易读性和跨平台访问性,让读者无论在哪里都能轻松...

Global site tag (gtag.js) - Google Analytics