apache+php配置关键代码

找到Apache的配置文件,在任意位置加入以下代码

#php module

LoadModule php5_module “E:/wamp/p/php5apache2_2.dll”
AddType application/x-httpd-php .php
PHPIniDir “E:/wamp/p/php.ini”

#php module end

程序的时间复杂度和空间复杂度

基本的计算步骤 

时间复杂度的定义
一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n))为算法的渐进时间复杂度(O是数量级的符号 ),简称时间复杂度。

根据定义,可以归纳出基本的计算步骤
1. 计算出基本操作的执行次数T(n)
基本操作即算法中的每条语句(以;号作为分割),语句的执行次数也叫做语句的频度。在做算法分析时,一般默认为考虑最坏的情况。

2. 计算出T(n)的数量级
求T(n)的数量级,只要将T(n)进行如下一些操作:
忽略常量、低次幂和最高次幂的系数

令f(n)=T(n)的数量级。

3. 用大O来表示时间复杂度
当n趋近于无穷大时,如果lim(T(n)/f(n))的值为不等于0的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n))。
一个示例:
(1) int num1, num2;
(2) for(int i=0; i<n; i++){
(3)     num1 += 1;
(4)     for(int j=1; j<=n; j*=2){
(5)         num2 += num1;
(6)     }
(7) }

分析:
1.
语句int num1, num2;的频度为1;
语句i=0;的频度为1;
语句i<n; i++; num1+=1; j=1; 的频度为n;
语句j<=n; j*=2; num2+=num1;的频度为n*log2n;
T(n) = 2 + 4n + 3n*log2n

2.
忽略掉T(n)中的常量、低次幂和最高次幂的系数
f(n) = n*log2n

3.
lim(T(n)/f(n)) = (2+4n+3n*log2n) / (n*log2n)
= 2*(1/n)*(1/log2n) + 4*(1/log2n) + 3

当n趋向于无穷大,1/n趋向于0,1/log2n趋向于0
所以极限等于3。

T(n) = O(n*log2n)

简化的计算步骤

再来分析一下,可以看出,决定算法复杂度的是执行次数最多的语句,这里是num2 += num1,一般也是最内循环的语句。

并且,通常将求解极限是否为常量也省略掉?

于是,以上步骤可以简化为:
1. 找到执行次数最多的语句
2. 计算语句执行次数的数量级
3. 用大O来表示结果

继续以上述算法为例,进行分析:
1.
执行次数最多的语句为num2 += num1

2.
T(n) = n*log2n
f(n) = n*log2n

3.
// lim(T(n)/f(n)) = 1
T(n) = O(n*log2n)

 

——————————————————————————–
一些补充说明
最坏时间复杂度
算法的时间复杂度不仅与语句频度有关,还与问题规模及输入实例中各元素的取值有关。一般不特别说明,讨论的时间复杂度均是最坏情况下的时间复杂度。这就保证了算法的运行时间不会比任何更长。

求数量级
即求对数值(log),默认底数为10,简单来说就是“一个数用标准科学计数法表示后,10的指数”。例如,5000=5×10 3 (log5000=3) ,数量级为3。另外,一个未知数的数量级为其最接近的数量级,即最大可能的数量级。

求极限的技巧
要利用好1/n。当n趋于无穷大时,1/n趋向于0

——————————————————————————–
一些规则(引自:时间复杂度计算 )
1) 加法规则
T(n,m) = T1(n) + T2(n) = O (max ( f(n), g(m) )

2) 乘法规则
T(n,m) = T1(n) * T2(m) = O (f(n) * g(m))

3) 一个特例(问题规模为常量的时间复杂度)
在大O表示法里面有一个特例,如果T1(n) = O(c), c是一个与n无关的任意常数,T2(n) = O ( f(n) ) 则有
T(n) = T1(n) * T2(n) = O ( c*f(n) ) = O( f(n) )

也就是说,在大O表示法中,任何非0正常数都属于同一数量级,记为O(1)。

4) 一个经验规则
复杂度与时间效率的关系:
c < log2n < n < n*log2n < n2 < n3 < 2n < 3n < n! (c是一个常量)
|————————–|————————–|————-|
较好                     一般              较差
其中c是一个常量,如果一个算法的复杂度为c 、 log2n 、n 、 n*log2n,那么这个算法时间效率比较高 ,如果是 2n , 3n ,n!,那么稍微大一些的n就会令这个算法不能动了,居于中间的几个则差强人意。
————————————————————————————————–
复杂情况的分析

以上都是对于单个嵌套循环的情况进行分析,但实际上还可能有其他的情况,下面将例举说明。

1.并列循环的复杂度分析
将各个嵌套循环的时间复杂度相加。

例如:

for (i=1; i<=n; i++)
x++;

for (i=1; i<=n; i++)
for (j=1; j<=n; j++)
x++;

解:
第一个for循环
T(n) = n
f(n) = n
时间复杂度为Ο(n)

第二个for循环
T(n) = n2
f(n) = n2
时间复杂度为Ο(n2)

整个算法的时间复杂度为Ο(n+n2) = Ο(n2)。

2.函数调用的复杂度分析
例如:
public void printsum(int count){
int sum = 1;
for(int i= 0; i<n; i++){
sum += i;
}
System.out.print(sum);
}

分析:
记住,只有可运行的语句才会增加时间复杂度,因此,上面方法里的内容除了循环之外,其余的可运行语句的复杂度都是O(1)。
所以printsum的时间复杂度 = for的O(n)+O(1) = 忽略常量 = O(n)

*这里其实可以运用公式 num = n*(n+1)/2,对算法进行优化,改为:
public void printsum(int count){
int sum = 1;
sum = count * (count+1)/2;
System.out.print(sum);
}
这样算法的时间复杂度将由原来的O(n)降为O(1),大大地提高了算法的性能。

3.混合情况(多个方法调用与循环)的复杂度分析
例如:
public void suixiangMethod(int n){
printsum(n);//1.1
for(int i= 0; i<n; i++){
printsum(n); //1.2
}
for(int i= 0; i<n; i++){
for(int k=0; k
System.out.print(i,k); //1.3
}
}
suixiangMethod 方法的时间复杂度需要计算方法体的各个成员的复杂度。
也就是1.1+1.2+1.3 = O(1)+O(n)+O(n2) —-> 忽略常数 和 非主要项 == O(n2)

————————————————————————————————–
更多的例子

O(1)
交换i和j的内容
temp=i;
i=j;
j=temp;

以上三条单个语句的频度为1,该程序段的执行时间是一个与问题规模n无关的常数。算法的时间复杂度为常数阶,记作T(n)=O(1)。如果算法的执行时间不随着问题规模n的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个较大的常数。此类算法的时间复杂度是O(1)。

O(n2)
sum=0;                /* 执行次数1 */
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
sum++;       /* 执行次数n2 */
解:T(n) = 1 + n2 = O(n2)

for (i=1;i<n;i++)
{
y=y+1;        ①
for (j=0;j<=(2*n);j++)
x++;        ②
}
解:  语句1的频度是n-1
语句2的频度是(n-1)*(2n+1) = 2n2-n-1
T(n) = 2n2-n-1+(n-1) = 2n2-2
f(n) = n2
lim(T(n)/f(n)) = 2 + 2*(1/n2) = 2
T(n) = O(n2).

O(n)
a=0;
b=1;                     ①
for (i=1;i<=n;i++) ②
{
s=a+b;    ③
b=a;     ④
a=s;     ⑤
}
解:  语句1的频度:2,
语句2的频度:n,
语句3的频度:n,
语句4的频度:n,
语句5的频度:n,
T(n) = 2+4n
f(n) = n
lim(T(n)/f(n)) = 2*(1/n) + 4 = 4
T(n) = O(n).

O(log2n)
i=1;       ①
while (i<=n)
i=i*2; ②
解: 语句1的频度是1,
设语句2的频度是t,  则:nt<=n;  t<=log2n
考虑最坏情况,取最大值t=log2n,
T(n) = 1 + log2n
f(n) = log2n
lim(T(n)/f(n)) = 1/log2n + 1 = 1
T(n) = O(log2n)

O(n3)
for(i=0;i<n;i++)
{
for(j=0;j<i;j++)
{
for(k=0;k<j;k++)
x=x+2;
}
}
解:当i=m, j=k的时候,内层循环的次数为k当i=m时, j 可以取 0,1,…,m-1 ,  所以这里最内循环共进行了0+1+…+m-1=(m-1)m/2次所以,i从0取到n, 则循环共进行了: 0+(1-1)*1/2+…+(n-1)n/2=n(n+1)(n-1)/2次
T(n) = n(n+1)(n-1)/2 = (n3-n)/2
f(n) = n3
所以时间复杂度为O(n3)。

 

————————————————————————————————–

php输出控制

1、简介

当PHP脚本有输出时,输出控制函数可以用这些来控制输出。这在多种不同情况中非常有用,尤其是用来在脚本开始输出 数据后,发送http头信息到浏览器。输出控制函数不影响由 header()或setcookie()发送的文件头信息,仅影响像echo这样的函数和PHP代码块间的数据。

 

2、作用

1)、如官方介绍说,为了避免在输出文件头信息如header()和setcookie()之前出现输出而发生的错误。可以使用输出缓存函数。如下代码

1
2
3
4
5
<?php 
    ob_start();      //打开缓存控制函数
    echo 'hello';
    header('Location: http://www.example.com/');
?>

2)、将输出内容放入缓存区,如需进行其他操作,如将输出写入缓存文件,可将缓冲区内容写入缓存文件,然后输出。

1
2
3
4
5
    ob_start();
    echo 'hello';
    $file = ob_get_contents();
    ob_end_clean();
    write_cache($file);    //将缓冲区内容写入缓存函数,实现细节省略

3)、获取某些无返回值内置函数的输出内容,如phpinnfo();

1
2
3
4
5
6
7
<?php
ob_start();
phpinfo();
$file = ob_get_contents();   //phpinfo()输出内容
ob_end_clean();
 
?>

4)、在使用框架的时候,我们会发现框架中会将输出缓冲然后进行变量替换。最后输出。

除了这些内容,我们还可以利用输出缓存进行错误处理,具体看代码。我们可以将错误信息获取,以更加友好的方式输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
 
function display_error()
{
	if($errors = error_get_last())
	{
		return var_export($errors , TRUE);
	}
	return FALSE;
}
 
$dis_error = 'display_error';
 
//$a = 3;
 
ob_start( $dis_error );
 
echo $a;

附:ob_start()官方解释:

1
bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )

此函数将打开输出缓冲。当输出缓冲激活后,脚本将不会输出内容(除http标头外),相反需要输出的内容被存储在内部缓冲区中。

内部缓冲区的内容可以用 ob_get_contents() 函数复制到一个字符串变量中。 想要输出存储在内部缓冲区中的内容,可以使用 ob_end_flush() 函数。另外, 使用 ob_end_clean() 函数会静默丢弃掉缓冲区的内容。

output_callback
可选参数 output_callback 函数可以被指定。 此函数把一个字符串当作参数并返回一个字符串。 当输出缓冲区被( ob_flush(), ob_clean() 或者相似的函数)冲刷(送出)或者被清洗的时候;或者在请求结束之际输出缓冲区内容被冲刷到浏览器的时候该函数将会被调用。 当调用 output_callback 时,它将收到输出缓冲区的内容作为参数 并预期返回一个新的输出缓冲区作为结果,这个新返回的输出缓冲区内容将被送到浏览器。 如果这个 output_callback 不是一个可以调用的函数,此函数 会返回 FALSE 。

如果回调函数有两个参数,第二个参数会由一个位域补充,该位域由 PHP_OUTPUT_HANDLER_START, PHP_OUTPUT_HANDLER_CONT 和 PHP_OUTPUT_HANDLER_END 组成。

如果 output_callback 返回 FALSE ,其原来的输入 内容被直接送到浏览器。

这个参数 output_callback 可以通过直接给一个 NULL 值而避开。

ob_end_clean(), ob_end_flush(), ob_clean(), ob_flush() and ob_start() 不能从一个回调函数中调用。 如果从回调函数中调用了它们,产生的行为是不明确的。 如果想要删除缓冲区的内容,从回调函数中返回一个”" (空字符串)。 更不能从一个回调函数中使用像print_r($expression, true) 或highlight_file($filename, true) 一样的输出缓冲函数。

网站的前台和后台

今天早上想如何安排网站的前台和后台结构,也看了不少网站的结构。

很多网站一般都会将前台和后台分开开发,至少前台会取一个文件夹叫application,后台取一个文件夹叫admin。然后各自开发各自的。

但是,其实前台和后台是一个道理,不一样的只是一般后台会有在每次点击菜单的时候进行登录检测和权限认证。

今天的目标就是写一个公共后台类,进行登录操作,以及登录检测,使得每一次后台按钮触发都要进行登录检测。

使用的框架是CI。

CI控制器写法

今天闲来无事,想用CI写个用户头像上传切割功能,代码写好了,在往CI整合的时候老是出现BUG。

找了半天发现了个非常低级的错误。

首页代码如下

<?php if ( ! defined(‘BASEPATH’)) exit(‘No direct script access allowed’);

class index extends CI_Controller{
public function index()
{
$pre_jcrop_url = “./static/plugins/jcrop_zh”;
$this->load->view(‘welcome_message’);
}
}

看这代码写的213不。就这样我排错排了一个小时。

突然发现,我Q,类名和方法名一样,这样不就相当于子类有了自己的构造器了吗?

那么,怎么避免呢?

两种方法,一种是在子类中添加如下代码

public function __construct() {
        parent::__construct();
    }
还有一种就是避免类名和方法名重复。

php框架思想之我见

php学习的方法有很多种,当然不论什么技术或者学问,它们的学习方法也是各式各样,而且是因人而异的。对于我来说,我觉得不论学习什么,既要站在门内,又要站在门外,就像“不识庐山真面目,只缘身在此山中”。人们往往过分的钻到山里而找不到出路,为了登山而登山,全然不知道自己要爬的是什么样子的一座山。现在的社会不同以前,隔行如隔山的年代已经过去,反而现在真正有所成就的都是门外汉?为什么呢?在于一个人对一个未知行业的全新认识和视角,这一点,很关键。

php是一门脚本语言,因为其底层是用c实现的,所以和c的语法很像(虽然我大学学的C全还给了老师)。php是一门相当松散的语言,像是沙滩上的一堆沙子,能不能垒成城堡?关键在于自己的创造力,用php实现什么都很好实现,但要写好一个很优秀的php项目,难度可想而知,这需要对山整体的认识和去山里探险的经验的把握,有的人对php的经验已经很熟,但是为什么写的项目很局限呢?因为他一直在山里刨坑,最后把自己埋在了php的这座山里。所以,我觉得这是学php最重要的,时时刻刻不要太过注重细节的实现,往往搭建一个良好的框架更重要。

php的框架不胜其数,我比较熟知的小的框架开说,国内的话,有thinkphp,国外用的比较多的就是CI,这些都是小项目开发使用,用的巧的话,可以承载一定规模的项目。对于框架,不能太依赖,重在学其原理,也就是当初为什么这么设计?我每当拿到一个东西的时候会想,这个东西为什么被开发出来,这样想问题,我觉得就像庖丁解牛一样,很多东西会有一种通透的感觉。再一点,框架就是框架,只是一个工具,你完全有能力把框架改成你喜欢的样式,这一点不难,要在开发环境中多去尝试。

很可惜,我到现在没有在工作环境中使用过CI或者TP,真正接触的一个CMS是phpcms V9这一版,当然这就不是框架的范围了。

在说框架之前,还有一个必须了解和明白的就是MVC。那么,什么是MVC,很多人会说M是模型,V是视图,C是控制器。我觉得这样的认识过于浅显。这一点是在我面试的时候认识到的,现在班照过来,MVC最初是M1,M1就是MVC三个问题放在一个文件中处理,传说中的混编;M2是把M这一层分出去;然后才到了MVC。我们用到的也就是MVC,它只是一种代码的规范,或者说叫种开发模式,至少我是这样理解的,因为MVC对用户是透明的,它的出现是为了在规范的基础上,高效的,集中的处理问题。各自做好各自的工作。让每一部分完成它应该完成的事情,这样说,就出来了PHP的类。

类是老生长谈的问题,我没有学过C++(还给了老师),也没有学过java(比较鄙视),我对类的理解可能比较局限,但是类就是进行职能分配的,它将实现层面的东西抽象化,把工作的重点放在了分配职能上,通俗的讲就是由以前的工人变成了工头,以前注重的是担水和泥,现在注重的是谁担水,谁和泥。它给了程序员一个很好的编程思维上的转向,将其进行一直抽象就出现了接口等,不过我认为接口对于php是鸡肋,php在照抄java。后来出现了命名空间等的概念,那会在接下来提到。

现在进入正题。CI为什么会出现?我在CI手册里看到介绍CI的文字我觉得最重要的就是两个词语,小巧和高效。小巧的体现是可以是代码库的大小,也可以从功能上说,CI主要做是的MV这两层,它没有所谓的模板解析,可以说是有点像M2的感觉只是将M分离出去,而不去处理V,将php代码直接写入模板中,CI为什么要这样做?因为高效,因为模板解析需要的是时间,那么什么是模板解析呢?或者说模板解析为什么会出现呢?

同样的道理,也是为了程序员开发的效率,因为没有一种语言去过渡php和静态的html,最粗暴的办法就是将文件命名为.php的文件,然后把html和php混编在一起,但这样不利于程序员的开发,因为现在的网站稍有规模就需要一个前端工程师,一个php工程师,很多前端工程师在不知道php语法的时候,如何去进行数据交流呢?我想这就是模板诞生的原因,模板是为了将V分离出来,呈现出一个简单的静态页,但是,模板解析附带出现了两个问题,一个是模板标签的定义,一个是解析模板标签带来的效率问题。在这种情况下,我想我会想到Smarty模板解析。它是一套比较全面的模板标签解析库,很多复杂的语法基本都能实现,事实上,CI+smarty开发环境是很常用的。CI专心做的事情是数据处理,它没有去考虑V层的东西,很可能在CI开发的时候,已经有了smarty模板,所以,CI就更注重自己关心的问题了。

那么,ThinkPHP呢?原理我想一样,但是ThinkPHP中加入了模板解析,虽然效率不高,但是至少这一点要比CI好,至于很多细节性的东西我就不多说,我只说一下一个框架运行的过程是什么样子的。

这里说框架的运行过程,我想包括CI和TP。当用户访问一个URL的时候(因为这些都是单入口框架,所以,文件地址一般不变) ,最先去做的事情不是处理URl而是进行框架初始化,初始化包括定义框架用到的各种常量,以及预加载一些框架需要类等等,就像搭个舞台一样。这里有一点需要提及的是,框架虽然有很多目录,很多目录下的文件,但是我想,因为有了包含文件函数,可以认为在程序运行的时候,这就是一份文件,我觉得这一点很重要。等预加载完成以后,一般会使用控制器相关的类进行url解析,以及路由调度;这步主要是为了搞清楚,用户想让我干什么?也就是url在请求什么。接下来就是把想应的控制器包含进来,进行初始化,在这个过程中如果需要M层的话,将对应的M层的类加载进来获取数据,在这个过程中我们需要注意的是在合适的时候做合适的事,写代码的时候要把职能划分清楚,你写M主要是干什么的?你写C主要是干什么的?这点很重要。还有两点,一点是一个框架额外的一些类和函数,这些类可以用于性能测试,加快开发速度,优化自己的代码;还有一点就是框架的规则,一个框架有一个框架的各种规则,很简单的比如,CI是如何命名自己写的一个控制器的,TP又是如何命名的,这是学习框架必须学会的,主要是要看它怎么实现,如果不喜欢,则自己改了就行。

大体把我对框架的认识说了一下,虽然本人php也不行,但是我还没有见过好的CI或者TP项目。以前看过一个CI的开源项目叫stblog,一看就觉得受到了框架很大的约束,我们使用框架是为了提高开发速度,不能为了使用而使用。顺便吐槽一下TP,我觉得TP3.1.3这一版很优秀,但是我觉得对于一个小型的框架来说,完全没有必要加入命名规则,因为根本用不着啊,不要为了加而加,这样加入没有解决实际的问题,反而增加了我们的学习成本。

当明白一个框架的思想的时候,最关键的就是实践,要跑到山里去,但一定不要把山的本来面目忘掉。

 

php代码规范

命名规范

Θ 类文件都以.class.php为后缀,使用驼峰法命名,并且首字母大写,例如 Pay.class.php;
Θ 类名和目录_文件名一致。例如:类名Zend_Autoloader的目录是Zend/Autoloader.class.php;
Θ 函数的命名使用小写字母和下划线的方式。例如:get_client_ip;
Θ 方法的命名使用驼峰法,首字母小写或者使用下划线”_”,例如listComment(),_getResource(),通常下划线开头的方法属于私有方法;
Θ 属性的命名使用驼峰法,首字母小写或者使用下划线”_”,如$username,$_instance,通常下划线开头的属性属于私有属性;
Θ 常量以大写字母和下划线”_”命名,如”HOME_URL”; 阅读详细 »

HTTP请求状态码

消息(1开头)
100 Continue

客户端应当继续发送请求。这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。服务器必须在请求完成后向客户端发送一个最终响应。
101 Switching Protocols

服务器已经理解了客户端的请求,并将通过Upgrade 消息头通知客户端采用不同的协议来完成这个请求。在发送完这个响应最后的空行后,服务器将会切换到在Upgrade 消息头中定义的那些协议。
只有在切换新的协议更有好处的时候才应该采取类似措施。例如,切换到新的HTTP 版本比旧版本更有优势,或者切换到一个实时且同步的协议以传送利用此类特性的资源。
102 Processing

由WebDAV(RFC 2518)扩展的状态码,代表处理将被继续执行。 阅读详细 »

ThinkPHP3.1.3笔记(一)

   主题:ThinkPHP框架缓存,ThinkPHP项目布局
   框架只是工具,它只是将你所经常用的代码按照一定的模式整理起来。方便程序员快速开发,也方便团队协调工作。但框架的基础是语言。如果连PHP有多少个超级全局变量都不知道的话,还是需要返回去好好打打基础。磨刀不误砍柴工。
阅读详细 »

apache配置虚拟域名

当自己的项目过多的时候,总是先访问localhost,然后寻找工作目录显得很费事。所以配置自己的虚拟域名是很有必要的。
1)打开虚拟域名的配置,文件位于apache配置文件httpd.conf里,如图,将注释去掉

2)按照Include文件路径找到虚拟域名的配置文件,添加如下代码,

1
2
3
4
5
6
7
8
    <VirtualHost *:80>
    ServerAdmin webmaster@dummy-host.admin.com
    DocumentRoot "D:/workplace/Aimee/upload"   #虚拟域名指向的目录
    ServerName www.Aimee.com                   #你的域名
    ServerAlias www.Aimee.admin.com
    ErrorLog "logs/www.Aimee.admin.com_error.log"
    CustomLog "logs/www.Aimee.admin.com_access.log" common
    </VirtualHost>

3)如果是windows系统,需要配置c:/windows/System32/Drivers/etc/hosts(需要取得管理员运行权限)

1
   127.0.0.1       www.aimee.com

然后重启自己的apache。配置完成

递归json

一个实现json的类
阅读详细 »

表单令牌防止重复提交原理

在生成表单的时候,为防止表单重复提交。在form表单中添加一个隐藏的input标签来存放令牌,等到提交的时候,和表单一起提交。提交以后和生成的session值作比较,通过这种方式来达到防止重复提交的目的。简要代码如下,
阅读详细 »