web上存漏洞及原理分析、防范方法(文件名检测漏洞)

我们通过前篇:web上存漏洞及原理分析、防范方法 学习,已经知道后端获取服务器变量,很多来自客户端传入的。跟普通的get,post没有什么不同。下面我们看看,常见出现漏洞代码。

1、检测文件类型,并且用用户上存文件名保存

if(isset($_FILES['img']))
{
    $file = save_file($_FILES['img']);
	if($file===false) exit('上存失败!');
	
	echo "上存成功!",$file;
}

function check_file($img)
{
	///读取文件
	if($img['error']>0) return false;
	
	$tmpfile = $img['tmp_name'];
	$filename = $img['name'];
	
	
	///读取文件扩展名
	$len=strrpos($filename,".");
	if($len===false) return false;
	
	//得到扩展名
	$ext = strtolower(substr($filename,$len+1));
	if(!in_array($ext,array('jpg','jpeg','png'))) return false;
	return true;
}

function save_file($img)
{
	if(!check_file($img)) return false;
	
	//格式检测ok,准备移动数据
	$filename = $img['name'];
	$newfile = "upload/" .$filename;
	if(!move_uploaded_file($img["tmp_name"],$newfile)) return false;
	
	return $newfile;

}
?>

以上代码,对输入类型也做了判断,看了没有问题。但是问题,确恰恰出现在对获取的用户名变量检测上面。直接获取传入用户名,然后存为文件。 有朋友会说:这些文件名都是我电脑里面存在的,文件名格式都受限于操作系统对文件名定义。 但是,需要注意是,对于$_FILES里面获取变量,是直接来自http request请求。它跟普通获取其它get,post变量一样。 因此,别有用心的人,往往会自己模拟浏览器,给服务器发送一个特殊文件名。然后,让存文件时候,能够正常保存为自己格式。

前些年,”\0” 在字符串中,保存为文件,会自动截断后面内容。 如:$filename 构造为:”a.php\0.jpg” ,我们想想,将会变成怎么样?

$newfile = “upload/a.php\0.jpg” 因为,对扩展名验证,最右边”.”后面字符是jpg ,是允许图片格式。 但是,我们一以该文件名,保存。 发现磁盘会在upload目录下面生成a.php ,\0后面所有字符,被自动截断。

该漏洞,风靡一时。当时几乎大多数上存网站都有漏洞。一时,很多平台关闭了上存。其实,根本原因就在此。我们拿到文件名,自己作为最终生成文件名保存了。  好的方法,是自己随机生成文件名+读取扩展名 。这样可以组织输入特殊字符,在进行文件保存时候,被抛弃或截断了。

php4时代这个漏洞可以利用,到php5时代,生成的变量文件名值中,会自动过滤掉”\0” ,这样无论用户构造怎么样的特殊”\0”用户名,都会被截断。 但是 ,目前这类漏洞,在asp,jsp 等站点。还经常有出现。老版本的php站点也会经常出现。

 

好了,今天先到这里,后面还有2种其它常见方法,后面给出!欢迎交流!

Posted in web安全 | Tagged , | 2 Comments

PHP 站点相对包含,路径的问题解决方法(include,require)

以前看了,很多框架,基本上很少使用相对路径包含。而一般很多做php web站点,喜欢用相对路径。 认为这样,无论目录放到那里。 只要跟另外目录关系一致。那么就不会出现问题。如果一个站点,一般都认为,如果用根目录,经常会改变网站地址,觉得很不方便。其实,我们从各大常见框架里面会发现,基本上都是采用是绝对路径方法。

  • 相对路径带来问题

我们有如下结构的目录。

<web>(网站根目录)
├<a>文件夹
│ │
│ └a.php
├<b>文件夹
│ │
│ └b.php
└test.php

 

如果b.php 包含a.php (include(“../a/a.php”)) ,然后test.php 包含b.php (include(“b/b.php”)) ,我们发现很奇怪问题。

首先访问:b.php 可以正常访问, 然后访问test.php

image

发现,找不到 a.php了。 这里注意一个问题是:php 默认相对路径都是以,被访问页面所在路径为准的。无论一个入口页面,里面包含多少文件,相对路径,都是以这个页面为准

如果访问test.php 根路径是:test.php,如果访问b.php 相对路径都以b.php 所在路径为准。刚刚页面test.php 包含了b.php,b.php包含了a.php. 所有包含都以test.php 为准的。

估计刚刚开始php学习朋友,经常遇到这个问题,而且发现经常出现一大堆警告影响大家学习的兴趣。

  • 使用绝对路径方法

各大开源框架基本上采用绝对路径方法,这样可以避免相对路径因为包含访问文件变了。基准路径变化,让包含出现错误了。 所以,我们看看常见方法。

首先将网站基准订到一个固定文件。一般可以用下面方法实现。如:根目录下面有个config.php文件。

<?php

define(‘Root_Path’,dirname(__FILE__));

__FILE__ 至的是当前脚本路径,在那个脚步php里面调用该变量,它的值就是该脚步的绝对路径。

然后,任何其它页面,在做包含时候,只需要包含了该config.php后。

<?php

包含config.php…..

include(Root_Path.”/文件路径”);即可

 

  • 使用绝对路径好处

使用解决路径好处除了可以在大型项目中,包含时候更准确定位到文件,不易产生错误外。还有另外一个好处,包含文件,性能会得到很大提升。

如果给一个相对位置包含,php查找该文件,一般会在set_include_path 函数,设置的所有路径里面去搜索。 我们知道,要一个一个去尝试,列举目录,然后查找文件。这直接会消耗大的IO。 也会消耗很多性能。 如果我们用绝对包含,直接就可以准确判断出,文件是否存在。不会去set_include_path设置目录去查找了。

以上问题,对于刚刚接触到php大型项目开发,可能会很容易遇到。欢迎讨论!

Posted in web原理, web性能, 学习心得 | Tagged | Leave a comment

windows live writer 写wordpress 博客

一直以来,在网上编辑器里面写wordpress 博客,就怕没有保存丢失内容,还有就是大量的图片上存。排版加水印等也比较复杂。 一直寻找可客户端软件,能够通过软件写wordpress博客。也可以在线管理博客!

其实,win7 自带的live write 就可以很好完成,而且它有丰富的插件。能够很方便完成图片上存,代码着色等工作。下面我们逐一介绍,希望对大家有帮助。

  • 配置windows live writer 管理wordpress

1、添加帐号

image

2、配置日志

image

image

image

3、开始写自己的博客了

image

 

  • 使用技巧

1.用户live wirter 多了。你会发现软件里面有,编辑模式、预览模式、源代码模式,可以很方便切换各种模式

2.图片处理简单多了,可以把任意图片,剪切粘贴 到文本任意地方。可以拖地位子,缩放图片。 也可以加上水印。我是live writer 2012版本。 最新版使用跟word类似,排版很方便。

3.你可以到官方网站:http://plugins.live.com/writer/browse?orderby=featured&page=1 下载工具插件,能够实现多种功能

 

后记:

可能你发现你的wordpress 不支持live writer 或者出错了。先找找看,你根目录有没有:xmlrpc.php。 下面我会分享一些,优秀的live wirter 博客插件。

Posted in 学习心得 | Tagged | Leave a comment

web上存漏洞及原理分析、防范方法

网站提供上存功能,是很多站点经常会有功能,商城,论坛还有常见一些网盘站点。常见互联网上面,我们也是经常听说,某某站点出现上存漏洞,某某开源项目有上存漏洞。 从互联网开始出现动态程序,上存漏洞像幽灵一样,频繁的出现在各种系统中。为什么,一个上存漏洞会这么频繁出现呢。而且,有些系统反复修补,多次还没有修补成功!其实主要问题,还是出现在上存原理上面。我们先看看,上存过程。

  • 网站上存过程分析

<?php
header("Content-type: text/html; charset=utf-8"); 
if($_FILES)
{
	echo '<pre>';
	var_dump($_FILES);
	echo '</pre>';
}
?>
<form action="" enctype="multipart/form-data" method="POST">
<input type="file" name="txt" size="50">
<input name="Submit" type="submit" value="提交" >
</form>

 

以上是个简单测试例子,我们看看

image

我们来分析下:

name来自 上存时候选择文件名称

type 是文件类型, 这个类型那里来的呢?呵呵,这里非常关键的。 很多时候,很多同人会认为,这个是服务器自动判断生成的。 如果是这样想,觉得也有可能。 php 封装了上存,它自带类库好像可以的。  但是,反过来想想,你随便选择个什么格式文件,都会有格式类型。这样一想,我们推断这个值,可能也来自用户输入的。 我们来抓包看看我们类型。

image

type值 也来自,用户输入值了。

size来自程序计算上存文件大小,这个是自动计算的。 相比我们都清楚的。

  • 你说这么多有啥样呢?

想必有朋友开始问了,我上面分析上存文件格式 ,跟开发出现漏洞有什么关联呢。 其实,我们想想,上面说的,一个文件名称name属性,以及文件类型type属性。 在我们上存文件,后端处理时候,会经常用到的。 作为开发者,在使用这2个变量,一定要继续执行“一切输入代码是有害的”原则。你在使用时候,要把它当作,跟其它get,post 获取变量一样处理。 要检测,过滤一切输入变量。

我们经常做功能是,限定用户输入必须是某种格式文件,然后保存为该格式。分析到这里,下面有很多需要继续分析地方,今天先提出大纲。接下来会分析这样常见3类上存漏洞。

1. 使用用户文件名,生成文件,特殊字符过滤不严格,导致文件生成出现漏洞

2.移动文件目录时候,由于采用用户传入的文件名拼接,生成到错误目录

3.相信用户输入type 类型,直接将用户文件名保存为文件

 

好了,下一篇我会举例逐步分析这3种常见漏洞,欢迎朋友们提出你的观点,与我交流!

Posted in web安全 | Tagged , | 1 Comment

web站点获取用户IP的安全方法 HTTP_X_FORWARDED_FOR检验

通过上一篇,获取用户Ip地址通用方法常见安全隐患(HTTP_X_FORWARDED_FOR) ,我们已经意 识到直接从http_x_forwarded_for中读取用户IP,跟我们直接从一个get,post值中读取其实没有两样。web参数检测里面一个基本原则:“一切输入都是有害的”,因此,只要是输入我们就需要进行过滤。

  • 安全过滤后的getIP函数

  function getIP() {
	$realip = ''; //设置默认值
	if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
		$realip = $_SERVER['HTTP_X_FORWARDED_FOR'];
	} elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
		$realip = $_SERVER['HTTP_CLIENT_IP'];
	} else {
		$realip = $_SERVER['REMOTE_ADDR'];
	}

	preg_match('/^((?:\d{1,3}\.){3}\d{1,3})/',$realip,$match);
	return $match?$match[0]:false;
}

 

以上函数,增加了IP判断,只会读取以Ip格式数据开头,并且第一个满足IP格式值。如果没有返回false。 这样就可以读取到满足格式的IP,验证了数据的IP格式。

  • 如果我读取互联网的IP,用户传入局域网的IP,我应该直接过滤掉

我们在一些网站上面,经常可以看到提示,非法的IP地址,其实一部分是IP地址格式错误,一部分可能是读取到IP地址,不满足互联网上面允许IP格式。 以下这个函数,是通过IANA站点规范,封装了个函数。 通过输入IP地址,能够准确知道,该IP是不是可以在互联网应用。

 

//互联网允许使用IP地址
function ipType2($ip) {
	$iplist = explode(".", $ip);

	if ($iplist[0] >= 224 && $iplist[0] <= 239)
		return '多播';
	if ($iplist[0] >= 240 && $iplist[0] <= 255)
		return '保留';

	if (preg_match('/^198\.51\.100/', $ip))
		return 'TEST-NET-2,文档和示例';
	if (preg_match('/^203\.0\.113/', $ip))
		return 'TEST-NET-3,文档和示例';

	if (preg_match('/^192\.(18|19)\./', $ip))
		return '网络基准测试';

	if (preg_match('/^192\.168/', $ip))
		return '专用网络[内部网]';

	if (preg_match('/^192\.88\.99/', $ip))
		return 'ipv6to4中继';
	if (preg_match('/^192\.0\.2\./', $ip))
		return 'TEST-NET-1,文档和示例';
	if (preg_match('/^192\.0\.0\./', $ip))
		return '保留(IANA)';
	if (preg_match('/^192\.0\.0\./', $ip))
		return '保留(IANA)';

	if ($iplist[0] == 172 && $iplist[1] <= 31 && $iplist[1] >= 16)
		return '专用网络[内部网]';

	if ($iplist[0] == 169 && $iplist[1] == 254)
		return '链路本地';
	if ($iplist[0] == 127)
		return '环回地址';
	if ($iplist[0] == 10)
		return '专用网络[内部网]';
	if ($iplist[0] == 0)
		return '本网络(仅作为源地址时合法)';

	return 'InterNet网地址';
}

当你输入IP地址,它返回是“’InterNet网地址’ ,那么这个IP地址不光格式正确,而且是互联网上面合法的IP地址。 这个函数很复杂,其实就是排除很多非互联网使用IP地址。 我们常见的192,127,10开头地址估计都很熟悉了。 但实际上,很多IP地址是保留的,或者留作它用。 不能作为互联网 IP使用。 有了以上两个函数,我们不光可以读到正确格式IP地址,还能够保证读到是互联网上面IP地址。 以上是工作中常使用的函数,欢迎朋友们交流!

Posted in web安全 | Leave a comment

获取用户Ip地址通用方法常见安全隐患(HTTP_X_FORWARDED_FOR)

  • 分析过程

这个来自一些项目中,获取用户Ip,进行用户操作行为的记录,是常见并且经常使用的。 一般朋友,都会看到如下通用获取IP地址方法。

function getIP() { 
	if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { 
		$realip = $_SERVER['HTTP_X_FORWARDED_FOR']; 
	} elseif (isset($_SERVER['HTTP_CLIENT_IP'])) { 
		$realip = $_SERVER['HTTP_CLIENT_IP']; 
	} else { 
		$realip = $_SERVER['REMOTE_ADDR']; 
		} 
		return $realip; 
	}

这个是网上常见获取,ip函数,用这些值获取IP,我们首先要弄清楚,这些数据是从那个地方传过来的。

  • IP获取来源

1.’REMOTE_ADDR’  是远端IP,默认来自tcp 连接是,客户端的Ip。可以说,它最准确,确定是,只会得到直接连服务器客户端IP。如果对方通过代理服务器上网,就发现。获取到的是代理服务器IP了。

如:a->b(proxy)->c  ,如果c 通过’REMOTE_ADDR’ ,只能获取到b的IP,获取不到a的IP了。

另外:该IP想篡改将很难实现,在传递知道生成php server值,都是直接生成的。

2.’HTTP_X_FORWARDED_FOR’,’HTTP_CLIENT_IP’ 为了能在大型网络中,获取到最原始用户IP,或者代理IP地址。对HTTp协议进行扩展。定义了实体头。

HTTP_X_FORWARDED_FOR = clientip,proxy1,proxy2  所有IP用”,”分割。 HTTP_CLIENT_IP 在高级匿名代理中,这个代表了代理服务器IP。既然是http协议扩展一个实体头,并且这个值对于传入端是信任的,信任传入方按照规则格式输入的。以下以x_forword_for例子加以说明,正常情况下,这个值变化过程。

image

 

  • 分析Bug风险点:

通过刚刚分析我们发现,其实这些变量,来自http请求的:x-forword-for字段,以及client-ip字段。 正常代理服务器,当然会按rfc规范来传入这些值。但是,当一个用户直接构造该x-forword-for值,发送给用户用户,那将会怎么样呢?

image图(1)

第2步,修改x-forword-fox值,我们看看结果

image

 

第三步,我们再修改下看看会怎么样?

image

 

哈哈,看到上面结果没,x-forwarded-for不光可以自己设置值,而且可以设置任意格式值。 这样一来,好比就直接有一个可以写入任意值的字段。并且服务器直接读取,或者写入数据库,或者做显示。它将带来危险性,跟一般对入输入没有做任何过滤检测,之间操作数据源结果一样。 并且容易带来隐蔽性。

  • 结论:

上面getip函数,除了客户端可以任意伪造IP,并且可以传入任意格式IP。 这样结果会带来2大问题,其一,如果你设置某个页面,做IP限制。 对方可以容易修改IP不断请求该页面。 其二,这类数据你如果直接使用,将带来SQL注册,跨站攻击等漏洞。至于其一,可以在业务上面做限制,最好不采用IP限制。 对于其二,这类可以带来巨大网络风险。我们必须加以纠正。

需要对getip 进行修改,得到安全的getip函数。

这类问题,其实很容易出现,以前我就利用这个骗取了大量伪装投票。有它的隐蔽性,其实只要我们搞清楚了,某些值来龙去脉的话。理解了它的原理,修复该类bug将是非常容易。

题外话,做技术,有三步,先要会做,会解决;后要思考为什么要这么做,原因原理是什么;最后是怎么样做,有没有其它方法。多问问自己,你发现距离技术真理越来越近。你做事会越来越得心应手的!

Posted in web安全 | Tagged | 1 Comment

wordpress 过滤器原理执行过程分析(源码分析)

从上一篇:wordpress 3.5 filter 消息标签的名称 ,我们知道了wordpress过滤器消息灵活性。接下来,我们分析下这个具体执行过程。这个也是wordpress 优秀代码一个核心点。学习并使用该方法,应用到自己的站点中,将会给自己的系统带来很大的灵活性提升,扩展性将大大增强。其实这 类设计模式,就是”监听者模式“,先让监听者监听某个事件,只要发生某个事情,就去执行任务。对一个事件,可以添加条监控,分别执行独立任务。 不过,消息过滤模式,它有一个特点,每个执行任务将会返回一个结果,这个结果将作为下一个任务输入参数。第一个任务输入参数,就是发送消息,并且传入的参数。

是不是感觉有点晕晕啦,呵呵,如果对监听者设计模式熟悉的话,比较容易理解的。 打个比方说,教室有张三在看小说,李四在聊天。但是,他们都怕老师进入教师。 因此都监听老师来了的事件。只要老师来了,张三就停止看小说,李四也不能聊天。 有个同学就望风,发现老是来了。 他喊一声:某某老是来了。 然后,张三,李四动作就激发啦。 望风报信同学,是消息的发送者。

实现类似上面2个功能,wordpress主要2个常用函数是:

add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) 添加消息主题$tag类型过滤器,过滤函数是:$function_to_add 该函数一定需要有返回值的;$priority是该函数执行优先级;数字越小将越先执行,accepted_args支持传入参数个数,默认是1。

apply_filters($tag, $value) ,发送过滤主题消息,$tag消息主题,$value 发送需要过滤内容。

 

PHP代码执行该过程原理

 

举例说明:

add_filter(‘the_title’,’filter_trim_title’,1,1);

实际实现类似:

$wp_filter[‘the_title’] []= array(‘function’=>’filter_trim_title’…) 用一个数组列表,记录消息所有映射函数,及所有传入参数

如果再有一条

add_filter(‘the_title’,’filter_substr_title’,1,1);

继续执行:

$wp_filter[‘the_title’] []= array(‘function’=>’filter_substr_title’…) ,该消息内容增加一条函数映射

如果这个时候,执行以下代码:apply_filters(‘the_title’,’testtest’),代码执行将会是类似如下:

$args=’testtest’;

foreach(add_filter(‘the_title’) as $the)

{

$args = call_user_func_array($the['function'], array_slice($args, 1, (int) $the(‘accepted_args’])); ///循环将该消息所有函数,执行一次,将返回的值作为下一个函数传入值。这里foreach前 会按照优先级排序

}

return $args; ///返回过滤后结果

 

 

通过上面例子,会发现,添加消息过滤方法,跟发送消息可以不同地方,只要放到一个全局数组中就可以了。 你只要在开始时候,增加过滤消息映射配置,可以变得更多行。 然后,可以再任意地方,发出这类消息,并且传入输入字符。 你会发现,通过该功能做消息的过滤,将是很轻松事情。

 

我们经常下会有从数据库里面读出代码,然后想做输出,这个时候,我们需要做转义操作。如果你的系统在读出内容后,添加了过滤消息发送接口,你需要对输出数据进行处理很容易。如下举例子

在通用包含地方添加如下代码(functions.php是不错选择)

 

add_filters(‘the_content’,’filter_contents’);

function filter_contents($data) 

{

     return htmlspecialchars($data);

}

然后在读取数据库地方加入如下代码

function get_content($data)

{

      return apply_filters(‘the_content’,$data);  //这样将$data作为消息发送,内容数据一起发送出去,所有绑定了’the_content’消息的所有函数,按照顺序串起来过滤内容。第一个过滤后结果,作为第2个输入参数,直到最后一个处理完,返回了内容。就是apply_filters 获得的内容。

}

 

 

觉得不错吧,这种解耦方法。扩展性大大增强,在你的项目中也用下类似方法吧。 其实实现过程很容易的。 但是,带来的扩展性就非常巨大了。其它处理消息过滤器函数,还有:has_filter() current_filter() merge_filters() remove_filter() remove_all_filters() 等,感兴趣,可以去研究下 它们源码!

不早了,昨晚休息比较晚,今天这个时候,感觉很困了。 欢迎与我交流!

Posted in web项目框架, 学习心得 | Tagged | Leave a comment

wordpress 3.5 filter 消息标签的名称

wordpress 框架有很多优点,从前一篇《我的博客》之从技术眼中分析wordpress结构优缺点” ,可以看到,它的消息标签在解耦方面扩展性有多强。

我看了,wordpress官方手册,里面没有对所有标签进行罗列。而如果你要进行wordpress插件开发,功能扩展开发。它的消息标签,是必须要有所了解的。 以下是我收集的所有filters类的消息标签,居然有500多个。

 

‘add_menu_classes’
‘admin_body_class’
‘admin_comment_types_dropdown’
‘admin_footer_text’
‘admin_memory_limit’
‘admin_post_thumbnail_html’
‘admin_title’
‘admin_url’
‘admired_attachment_size’
‘admired_author_bio_avatar_size’
‘admired_layout_classes’
‘admired_status_avatar’
‘admired_text’
‘akismet_comment_nonce’
‘akismet_delete_commentmeta_interval’
‘akismet_optimize_table’
‘akismet_show_user_comments_approved’
‘akismet_ua’
‘allowed_http_origin’
‘allowed_http_origins’
‘allowed_themes’
‘allow_subdirectory_install’
‘all_plugins’
‘all_themes’
‘attachment_fields_to_edit’
‘attachment_fields_to_save’
‘attachment_icon’
‘attachment_link’
‘attribute_escape’
‘auth_redirect_scheme’
‘autocomplete_users_for_site_admins’
‘blog_details’
‘blog_option_’
‘blog_redirect_404′
‘body_class’
‘browse-happy-notice’
‘bulk_actions-’
‘can_edit_network’
‘category_archive_meta’
‘category_description’
‘category_feed_link’
‘category_link’
‘close_comments_for_post_types’
‘comment_edit_pre’
‘comment_edit_redirect’
‘comment_form_default_fields’
‘comment_form_defaults’
‘comment_form_field_comment’
‘comment_form_logged_in’
‘comment_max_links_url’
‘comment_row_actions’
‘comments_array’
‘comments_open’
‘comments_per_page’
‘comments_popup_link_attributes’
‘comment_status_links’
‘comment_text’
‘contextual_help’
‘contextual_help_list’
‘core_version_check_locale’
‘cron_request’
‘cron_schedules’
‘customize_allowed_urls’
‘customize_value_’ . $this->id_data[ 'base'
'dashboard_incoming_links_feed'
'dashboard_incoming_links_link'
'dashboard_primary_feed'
'dashboard_primary_link'
'dashboard_primary_title'
'dashboard_secondary_feed'
'dashboard_secondary_link'
'dashboard_secondary_title'
'date_formats'
'dbdelta_create_queries'
'dbdelta_insert_queries'
'dbdelta_queries'
'default_content'
'default_contextual_help'
'default_excerpt'
'default_hidden_meta_boxes'
'default_option_'
'default_site_option_'
'default_title'
'delete_site_email_content'
'deprecated_argument_trigger_error'
'deprecated_file_trigger_error'
'deprecated_function_trigger_error'
'disable_captions'
'display_media_states'
'display_post_states'
'documentation_ignore_functions'
'doing_it_wrong_trigger_error'
'domain_exists'
'do_mu_upgrade'
'do_parse_request'
'dynamic_sidebar_params'
'editable_slug'
'edit_bookmark_link'
'edit_categories_per_page'
'edit_comment_link'
'editor_max_image_size'
'edit_post_link'
'edit_posts_per_page'
'edit_profile_url'
'edit_tag_link'
'edit_tags_per_page'
'edit_term_link'
'embed_56'
'embed_56com'
'embed_6cn'
'embed_cache_oembed_types'
'embed_defaults'
'embed_googlevideo'
'embed_handler_html'
'embed_ku6'
'embed_letv'
'embed_maybe_make_link'
'embed_oembed_html'
'embed_sina'
'embed_sohu'
'embed_tudou'
'embed_youku'
'embed_youtube'
'enable_edit_any_user_configuration'
'enable_post_by_email_configuration'
'enable_update_services_configuration'
'enter_title_here'
'esc_html'
'esc_textarea'
'exit_on_http_head'
'export_args'
'ext2type'
'extra_theme_headers'
'feed_content_type'
'force_filtered_html_on_import'
'format_to_edit'
'found_users_query'
'gallery_style'
'getarchives_join'
'get_archives_link'
'getarchives_where'
'get_attached_file'
'get_avatar_comment_types'
'get_bloginfo_rss'
'get_blogs_of_user'
'get_bookmarks'
'get_calendar'
'get_categories_taxonomy'
'get_comment_link'
'get_comment_text'
'get_delete_post_link'
'get_edit_bookmark_link'
'get_edit_comment_link'
'get_edit_post_link'
'get_edit_tag_link'
'get_edit_term_link'
'get_edit_user_link'
'getimagesize_mimes_to_exts'
'get_image_tag'
'get_lastpostdate'
'get_lastpostmodified'
'get_media_item_args'
'_get_page_link'
'get_pages'
'get_search_query'
'get_tags'
'get_terms_args'
'get_terms_fields'
'get_terms_orderby'
'gettext'
'gettext_with_context'
'get_the_author_'
'get_the_categories'
'get_the_excerpt'
'get_the_tags'
'get_the_terms'
'global_terms_enabled'
'graceful_fail'
'graceful_fail_template'
'hidden_meta_boxes'
'home_url'
'http_headers_useragent'
'http_origin'
'http_request_args'
'http_request_redirection_count'
'http_request_timeout'
'http_request_version'
'http_response'
'https_local_ssl_verify'
'icon_dir'
'icon_dirs'
'icon_dir_uri'
'image_add_caption_shortcode'
'image_downsize'
'image_editor_default_mime_type'
'image_make_intermediate_size'
'image_memory_limit'
'image_resize_dimensions'
'image_send_to_editor'
'image_send_to_editor_url'
'image_size_names_choose'
'import_upload_size_limit'
'install_plugins_nonmenu_tabs'
'install_plugins_tabs'
'install_themes_nonmenu_tabs'
'install_themes_tabs'
'intermediate_image_sizes'
'intermediate_image_sizes_advanced'
'is_email'
'is_email_address_unsafe'
'is_multi_author'
'is_protected_meta'
'jpeg_quality'
'js_escape'
'kses_allowed_protocols'
'lang_codes'
'list_cats'
'list_pages'
'load_default_embeds'
'load_image_to_edit_attachmenturl'
'load_image_to_edit_filesystempath'
'load_image_to_edit_path'
'load_textdomain_mofile'
'locale'
'login_form_bottom'
'login_form_defaults'
'login_form_middle'
'login_form_top'
'lostpassword_url'
'manage_media_columns'
'manage_pages_columns'
'manage_posts_columns'
'manage_' . $screen->id . '_columns'
'manage_sites_action_links'
'manage_taxonomies_for_attachment_columns'
'manage_users_custom_column'
'mce_css'
'mce_spellchecker_languages'
'media_meta'
'media_row_actions'
'media_send_to_editor'
'media_upload_mime_type_links'
'media_upload_tabs'
'media_view_settings'
'media_view_strings'
'mime_types'
'ms_user_row_actions'
'mu_dropdown_languages'
'mu_menu_items'
'myblogs_blog_actions'
'myblogs_options'
'nav_menu_attr_title'
'nav_menu_css_class'
'nav_menu_description'
'nav_menu_item_id'
'nav_menu_items_'
'nav_menu_meta_box_object'
'network_home_url'
'network_sites_updated_message_' . $_GET['updated']
‘network_site_url’
‘new_admin_email_content’
‘newblogname’
‘newblog_notify_siteadmin’
‘new_user_email_content’
‘newuser_notify_siteadmin’
‘next_comments_link_attributes’
‘next_posts_link_attributes’
‘ngettext’
‘ngettext_with_context’
‘nonce_life’
‘nonce_user_logged_out’
‘number_format_i18n’
‘oembed_dataparse’
‘oembed_fetch_url’
‘oembed_linktypes’
‘oembed_providers’
‘oembed_result’
‘option_’
‘option_enable_xmlrpc’
‘override_load_textdomain’
‘override_unload_textdomain’
‘page_attributes_dropdown_pages_args’
‘page_css_class’
‘page_link’
‘paginate_links’
‘pingback_ping_source_uri’
‘pingback_useragent’
‘pings_open’
‘plugin_install_action_links’
‘plugin_locale’
‘plugin_row_meta’
‘plupload_default_params’
‘plupload_default_settings’
‘plupload_init’
‘post_comments_feed_link_html’
‘post_date_column_time’
‘post_format_rewrite_base’
‘post_link_category’
‘postmeta_form_limit’
‘posts_request_ids’
‘post_thumbnail_html’
‘post_thumbnail_size’
‘post_type_archive_feed_link’
‘post_type_archive_link’
‘post_types_to_delete_with_user’
‘post_updated_messages’
‘pre_add_site_option_’
‘pre_comment_approved’
‘pre_ent2ncr’
‘pre_get_space_used’
‘pre_http_request’
‘pre_http_send_through_proxy’
‘pre_insert_term’
‘pre_option_’
‘pre_option_enable_xmlrpc’
‘pre_set_site_transient_’
‘pre_set_transient_’
‘pre_site_option_’
‘pre_site_transient_’
‘pre_transient_’
‘pre_update_option_’
‘pre_update_site_option_’
‘pre_upload_error’
‘pre_user_display_name’
‘preview_post_link’
‘previous_comments_link_attributes’
‘previous_posts_link_attributes’
‘print_scripts_array’
‘print_styles_array’
‘private_title_format’
‘protected_title_format’
‘pub_priv_sql_capability’
‘query’
‘quick_edit_dropdown_pages_args’
‘redirect_network_admin_request’
‘redirect_post_location’
‘redirect_user_admin_request’
‘request_filesystem_credentials’
‘role_has_cap’
‘rss_update_frequency’
‘rss_update_period’
‘safe_style_css’
‘salt’
‘sanitize_email’
‘sanitize_html_class’
‘sanitize_key’
‘sanitize_mime_type’
‘sanitize_trackback_urls’
‘sanitize_user’
‘screen_layout_columns’
‘screen_options_show_screen’
‘screen_settings’
‘script_loader_src’
‘search_link’
‘set_url_scheme’
‘show_admin_bar’
‘show_advanced_plugins’
‘show_network_site_users_add_existing_form’
‘show_network_site_users_add_new_form’
‘show_recent_comments_widget_style’
‘single_cat_title’
‘single_tag_title’
‘single_term_title’
‘site_option_’
‘site_transient_’
‘site_url’
‘smartadapt_attachment_size’
‘split_the_query’
‘status_header’
‘style_loader_src’
‘style_loader_tag’
‘stylesheet_directory’
‘stylesheet_directory_uri’
‘subdirectory_reserved_names’
‘tables_to_repair’
‘tag_archive_meta’
‘tag_cloud_sort’
‘tag_feed_link’
‘tag_link’
‘tag_row_actions’
‘tagsperpage’
‘taxonomy_feed_link’
‘teeny_mce_buttons’
‘teeny_mce_plugins’
‘template_directory’
‘template_directory_uri’
‘template_include’
‘term_name’
‘terms_clauses’
‘terms_to_edit’
‘the_author_posts_link’
‘the_category’
‘the_content_export’
‘the_content_more_link’
‘the_excerpt_export’
‘the_feed_link’
‘theme_action_links’
‘theme_install_actions’
‘theme_locale’
‘theme_root’
‘theme_root_uri’
‘theme_row_meta’
‘the_permalink’
‘the_search_query’
‘the_shortlink’
‘the_tags’
‘the_title’
‘the_title_rss’
‘thread_comments_depth_max’
‘time_formats’
‘transient_’
‘twentyeleven_attachment_size’
‘twentyeleven_author_bio_avatar_size’
‘twentyeleven_color_schemes’
‘twentyeleven_default_theme_options’
‘twentyeleven_header_image_height’
‘twentyeleven_header_image_width’
‘twentyeleven_layout_classes’
‘twentyeleven_layouts’
‘twentyeleven_status_avatar’
‘twentyeleven_theme_options_validate’
‘twentyten_attachment_height’
‘twentyten_attachment_size’
‘twentyten_author_bio_avatar_size’
‘twentyten_header_image_height’
‘twentyten_header_image_width’
‘twentytwelve_attachment_size’
‘twentytwelve_author_bio_avatar_size’
‘twentytwelve_status_avatar’
‘type_url_form_media’
‘update_attached_file’
‘update_feedback’
‘update_footer’
‘update_welcome_email’
‘update_welcome_subject’
‘update_welcome_user_email’
‘update_welcome_user_subject’
‘upload_dir’
‘upload_mimes’
‘upload_per_page’
‘upload_post_params’
‘upload_size_limit’
‘use_curl_transport’
‘use_default_gallery_style’
‘use_fsockopen_transport’
‘use_google_chrome_frame’
‘user_contactmethods’
‘user_dashboard_url’
‘user_has_cap’
‘user_row_actions’
‘use_streams_transport’
‘validate_current_theme’
‘validate_username’
‘views_’
‘walker_nav_menu_start_el’
‘whitelist_options’
‘widget_comments_args’
‘widget_posts_args’
‘widget_text’
‘widget_title’
‘wp_admin_bar_class’
‘wp_admin_css’
‘wp_admin_css_uri’
‘wp_cache_themes_persistently’
‘wp_checkdate’
‘wp_check_filetype_and_ext’
‘wp_check_post_lock_window’
‘wp_comment_reply’
‘wp_create_thumbnail’
‘wp_dashboard_widgets’
‘wp_default_editor’
‘wp_delete_file’
‘wp_die_ajax_handler’
‘wp_die_handler’
‘wp_die_xmlrpc_handler’
‘wp_dropdown_cats’
‘wp_edit_nav_menu_walker’
‘wp_editor_set_quality’
‘wp_feed_cache_transient_lifetime’
‘wp_fullscreen_buttons’
‘wp_generate_attachment_metadata’
‘wp_generate_tag_cloud’
‘wp_generator_type’
‘wp_get_attachment_image_attributes’
‘wp_get_attachment_link’
‘wp_get_attachment_metadata’
‘wp_get_attachment_thumb_file’
‘wp_get_attachment_thumb_url’
‘wp_get_attachment_url’
‘wp_get_nav_menu_items’
‘wp_get_nav_menus’
‘wp_get_update_data’
‘wp_handle_upload’
‘wp_handle_upload_prefilter’
‘wp_http_cookie_value’
‘wp_image_editors’
‘wp_insert_post_empty_content’
‘wp_insert_post_parent’
‘wp_is_large_network’
‘wp_kses_allowed_html’
‘wp_link_pages_args’
‘wp_list_bookmarks’
‘wp_list_categories’
‘wp_mail’
‘wp_mail_charset’
‘wp_mail_content_type’
‘wp_mail_from’
‘wp_mail_from_name’
‘wp_mime_type_icon’
‘wpmu_blogs_columns’
‘wpmu_delete_blog_upload_dir’
‘wpmu_drop_tables’
‘wpmu_signup_blog_notification_email’
‘wpmu_signup_blog_notification_subject’
‘wpmu_signup_user_notification_email’
‘wpmu_signup_user_notification_subject’
‘wpmu_users_columns’
‘wp_nav_menu’
‘wp_nav_menu_args’
‘wp_nav_menu_container_allowedtags’
‘wp_nav_menu_items’
‘wp_nav_menu_objects’
‘wp_network_dashboard_widgets’
‘wp_page_menu’
‘wp_page_menu_args’
‘wp_parse_str’
‘_wp_post_revision_fields’
‘wp_prepare_attachment_for_js’
‘wp_read_image_metadata’
‘wp_read_image_metadata_types’
‘_wp_relative_upload_path’
‘wp_setup_nav_menu_item’
‘wp_signup_location’
‘wp_sprintf’
‘wp_tag_cloud’
‘wp_terms_checklist_args’
‘wp_trim_words’
‘wp_unique_post_slug’
‘wp_unique_post_slug_is_bad_attachment_slug’
‘wp_unique_post_slug_is_bad_flat_slug’
‘wp_unique_post_slug_is_bad_hierarchical_slug’
‘wp_update_attachment_metadata’
‘wp_update_term_parent’
‘wp_upload_bits’
‘wp_user_dashboard_widgets’
‘wxr_export_skip_postmeta’
‘xmlrpc_blog_options’
‘xmlrpc_default_post_fields’
‘xmlrpc_default_posttype_fields’
‘xmlrpc_default_revision_fields’
‘xmlrpc_default_taxonomy_fields’
‘xmlrpc_default_user_fields’
‘xmlrpc_enabled’
‘xmlrpc_login_error’
‘xmlrpc_pingback_error’
‘xmlrpc_prepare_comment’
‘xmlrpc_prepare_media_item’
‘xmlrpc_prepare_page’
‘xmlrpc_prepare_post’
‘xmlrpc_prepare_post_type’
‘xmlrpc_prepare_taxonomy’
‘xmlrpc_prepare_term’
‘xmlrpc_prepare_user’
‘xmlrpc_wp_insert_post_data’

 

今天先做一个总列表,后面我会对一些filters 消息标签作用,用户做一些收集。 这类标签基本涵盖了wordpress 所有操作功能。 能够通过filter 消息标签,将所有功能发送数据进行一次过滤。然后再作用到目标,数据源可以来自前端,数据库,或者网络接口读取。也欢迎,朋友们一起来补充这些消息标签。

Posted in web项目框架 | Tagged | 1 Comment

《我的博客》之从技术眼中分析wordpress结构优缺点

我还是觉得自己开辟搭建一个独立blog平台,我常用linux服务器,因此选择php环境。 这样,我可以选择的开源博客系统会有很多。这里不对博客系统进行纵向对比。我说下,对wordpress认识过程,一些自己的体会。纯属代表个人意见,欢迎讨论!

 

刚开始,下载一套wordpress 3.5,然后一路安装下来,比较简单,跟常见其它一些开源平台查不到。 只要配置好数据库连接,然后一路下一步,分把钟就可以做好。 也是由于,对wordpress 早已经有所耳闻,而且感觉它名气很大,这类系统中佼佼者。

  • 首先看看它数据库结构吧

image

 

11张表,实现了强大的博客功能,首先表示不简单。现在很多cms 基本上都是40-50个表。其中很有几张表,设计为窄表,基本上类似,类型,键值名,键值。类似近年的NoSql类型数据库。都是键值对。读取灵活,扩展方便。 可能就是,开发时候复杂些。这类基本设计是:posts,postmeta表,所有文章都具备的通用字段,就放到posts表,而针对文章进行属性设置,如:访问数,tag,权限等。都放到postmeta表。如:

image

窄表,可以任意对一篇文章新增键值,扩展新的属性。 这也是wordpress 插件几乎可以做到无所能一个原因。 这类表可以非常灵活。新增一个属性,只需要在这个表里面,对posts ID 增加这类属性的一条记录。而且这类窄表,字段少,读取速度也很快。

感觉到表设计不足之处(个人认为,欢迎交流)

image

Posts表里面字段,一张表里面,居然有6个text 字段。如果读取时候,不小心select *,该会有多少磁盘IO,一条记录,该会占多少的数据块。记得刚刚,分析wordpress时候,当时一下子就看到这样情况。我毅然选择放弃该系统。也因此让我忽略了,他窄表设计优点。 个人认为,一些字段可以设置为varchar,另外text 可以移到单独posts_data表。里面只存放text字段,PostID 信息。欢迎高人指点。

 

  • 再看看它的代码吧

 

现在wordpress插件有成百上千个,它基本上可以做各种系统。除了blog外,它还被改成了新闻网站,企业网站,电影网站,甚至是商城系统。这些足以说明,他前端程序一定有过人的设计架构,有它足够灵活性。

  1. 看下他的目录结构吧

image

 

从这些目录,真的还没有发现它比较独特优越性。现在网上常见的MVC 结构,有独立模版目录。 这个wordpress,实际只能算,MC,而没有V,它实际上前端是通过php 直接调用include 目录里面函数而已。如:

image

这个也是它的一个独特特点了,国内很多模版,都会开辟自己的标签,如smarty的是:{$smarty.config.aa} 等。 而wordpress里面没有自己标签,要读取,通过函数去读。它省去了标签省去了大家学习标签成本,但大家也需要掌握N多的函数。 可以把它的函数,理解为其其它系统中定义标签。

无论是函数,还是标签,只要你在前端页面都使用这些方法,去读取你要的数据,实际上都对后端数据库做了隔离。从而让使用者不关系数据库数据了。 数据库结构修改,只要调用函数做相应跳转。前端页面还可以照样那样用。而不需要任何修改。从而保证它的稳定性。

image

它函数封装,不光做到从DB读取数据,通过函数,读模版,读公共包含文件,等等都做到了,函数封装。几乎做到了,所有读取数据封装(DB,file,request中),这样封装,在目前国内系统中很少见,因为为每个功能去写一堆函数,切实很多工作量。国内框架,基本上喜欢封个方法,做到很通用,然后自己去调去吧。 这个方法,可能可以读取用户表,也可以读评论,也可以读文章。 其实,看起来很通用,实际用起来就会很复杂,也会增加上手难度。我们看看wordpress 设计方法。

 

image

这只是其中的很少一部分函数,真正差不多1000把个函数呢。 因为,wordpress 函数几乎封装了所有数据调用,因此前端开发只用调用函数,就可以完成。简单容易,这也是灵活性,及得到广泛使用一个原因。

2.  看看它代码实现优点

如果说,wordpress 光只有,强大的含数据库,做到前端页面与数据直接分离。现在很多标签调用也能做到分离。 真正wordpress灵活性,以及能够扩展出各类插件,完成独特开发功能,与它独立系统分不开。

它有一个 消息机制,还有一个队列过滤模块,任务队列处理模块。这2个模块,都建立在它的一个消息机制上,普通开发方式如下:

消息机制,可以做到,前端跟实现地方解耦。这里打个比方,如一个用户完成注册,我要给用户发一个邮件,然后给管理员发一个站内信。我们以前做法是:

image

如果发完站内信后,还有发一个手机短信,我们必须修改注册逻辑,在接下来再增加一个发送手机短信功能。 这时,你发现任何前端修改,都需要在原先逻辑里面修改一通。 如果我们采用消息机制,程序将变为:

image

 

这个图,是改用消息模式结果,用户注册功能模块,跟接收到消息处理各个任务直接,代码没有直接关联。如果需要增加一个接收注册消息,给其它人通知一下,只需要新增一个模块,订制注册消息,然后处理一个新流程即可。

目前各类开源项目里面,消息机制已经变得非常常见了。 但在wordpress这个blog框架,它的消息主题非常完善。几乎任何动作都有消息主题发送消息。 这样让开发者,只要订制相关主题,就可以,增加自己额外处理功能。 例如: 用户发一个帖子后。检测下用户是不是有广告信息,只要订制:发帖消息,然后,增加新功能,检测内容。发现不满足,直接屏蔽帖子。

有人估计要说,这类功能,现在很多框架有类似东西,例如,页面开始有个start事件,结束有个end事件。 确实,消息概念在很多框架里面确实有用。 但是,没有这么完毕的消息主题抛出。 几乎做到整个系统任何操作,都能有消息,这样没有什么功能不能进行扩展了

有兴趣可以去研究下,wordpress add_filter,apply_filters ,还有add_action,apply_action ,只是收到消息后,一个按顺序进行一系列元内容过滤,一个是接收元内容,进行一系列任务。

wordpress 2大优点,一个是函数封装数据操作,一个是它完善消息功能。那它有何种确定呢?

  • wordpress源码不足

 

任何一个系统,有优点,一定有不尽人意的地方。

首先,因为它要做到自己很多函数,可以直接读取一些循环数据。或者直接读取数据库配置项。它启动时候,采用了大量的全局变量。一个大的blog,可能启动后全局变量有几百个。 有的是数据记录,有的是字符串,有的是对象。你可能会发现,这些数据可能有1,2M;无论有没有使用,这些都会先创建。可想而知,他将带来大量内存浪费。另外一个就是,可能会影响调用速度。 很多网友使用wordpress就明显有这个感觉了。

另外一个问题,其实正式来自它的消息处理模块。 它跟我们网上看到的不一样,它的消息模块是同步模块。以上面打的比方来说,尽管做到发送消息,跟处理模块代码分离。 但实际上执行过程没有多大变化,也是要先注册用户完,然后完成一些列处理功能。最好这个php页面才算处理完了。 需要时间还是那么些。 如果真的,能把这类跟功能无关接收消息,处理模块异步到后端。 那么估计再没有人抱怨wordpress打开一个要4,5秒了。

 

  • 后记:

 

就说这么些了,估计很多同人告诉我说。居然它性能不好,又慢。 你干什么还要用它呢? 这个就看你主要关注的是什么了,如果你需要快的开发模版,而且非常容易开发各种扩展。那么它是你最好选择了。 况且,性能问题,一但静态化后,几乎解决了。

估计还有人说,你既然提到它性能这么些不足,那么你有什么好的调优方法没有。 呵呵,这里欢迎大家交流意见,我也有一些想法,就先不详说,还不成熟。欢迎交流!!!祝朋友们,周末愉快!

Posted in web性能 | Tagged | 1 Comment

《我的博客》之选择公共博客平台还是自建博客站点

前言:看到很多同人有自己的博客,也知道它的重要性,以及带来诸多好处。 于是乎准备开始搭建一个自己的技术博客平台。分享经验教训,分享技术前瞻,聊聊自己对新鲜事务看法。 于是准备开始搭建平台了,首先遇到棘手问题就是,家按在那里呢?

博客安家在那里呢?

这个可能是很多朋友比较纠结的问题。自己建个博客站点,还是在公共平台申请呢? 可能很多朋友看到很多博客公共平台上面,有些用户流量很高,访问量也多,于是打算去申请一个。这里各有各的好处,各有各的问题。 以下分析来自个人观点,欢迎交流!

公共博客平台PK独立搭建博客

其实,我3年前在知名博客站点,开通了博客,大概2,3 个月, 排名就进入1000多位。 后来,一直没有打理,现在每天访问量,有近千UV,不过现在我打算独立出来,搭建自己博客站点。自己搭建博客系统,跟在公共平台建博客 不同之处。 好比是一个是专卖品牌店,一个就是超市柜台档口。超市当然有自己的名气,有自己的客户,基本上你不用太多打理,你访问量也不会太少。只要,你努力经验一番,很快你人气就会大涨。但是,这时你发现很多会被限制。如:你要扩张店面,你要开辟更多东西,你要做营销广告等等。 这个时候,你发现基本上很难加入,因为你的档口大小位置空间都固定了。另外一个就是,访问量多了,也很难打造自己的品牌。 大家更多记得是超时名称,大家买的都相同。他们可能记得更清楚是,某某超时里面有个蛋糕柜台,摊主是某某某。 当然,如果你另外开店面平台,前期投入会比较大,你需要去买空间、域名,需要取找程序代码,还可能需要请专业人员装修店面。 而且,你会发现你前期投入,因为你店面很偏僻,在很久很久时候,还很少有人来光顾。这个时候,你会发现,你的付出与回报不成正比。尽管,你发现很多个人名家博客,很强大,人气很多。但是,估计他们只是千千万万博主中,坚持下来的为少数。 很多博主,或许因为看不到黎明太阳,而在五更前将平台放弃了。也有朋友说,这类博客,一定要原创,一定要经常更新,一定要坚持一年以上,才会逐渐有人气。大多数的博主,都会很快失去信心,很快不能坚持下去。其实,这个时候,你坚持下去,增加自己的知识视野远远比带来人气提升价值大多了

先用公共平台后搭建独立博客平台

我的过程,可能也是很多朋友也是这样。 经常看到,默默很牛博客平台,上面某个博主搬家了。 然后,有一个自己独立博客站点。 我想这样兼顾了2种平台利弊。前期快速开展了自己的博客,锻炼了自己写作方面技巧,也积累一定人气,也有自己的朋友圈。这个时候,人气增长,发现对自己带来其它商业价值在受约束。 于是乎搭建自己的独立博客平台。 这时,你发现因为自己经验增强,对博客平台搭建等等可能更多了解。投入一小部分成本,很快有自己的平台。 由于自己在公共平台的人气,很快会带来一批用户过来。自己也会在独立平台,更卖力写作,创作。平台会逐步逐步提升,只要坚持漫长的一段时间,一定会越来越好!

后记:

以上是我的一个经验,欢迎交流!我也是后期打算建立自己的独立博客系统,接下来,我将继续分享,自己为什么选择wordpress原因,以及我对于该博客系统一些见解(从技术层面)。

Posted in 学习心得 | Tagged | Leave a comment