PHP的单例模式

单例模式被当作职责模式,被用来在应用程序中创建一个单一的功能访问点。比如,在OOP中,一个对象只负责一个特定的任务(一个对象去访问数据库等)。

作用:
1)节约系统资源。单例类仅有一个实例存在。
2)维持应用程序状态的同步

特点(至少以下四个特点):
1)必须拥有访问级别为private的构造函数。因为单例类不能在其它类中直接实例化,只能被其自身实例化。
2)拥有一个保存类的实例的静态成员变量。
3)拥有一个访问实例的公共的静态方法。
4)必须创建一个空的访问级别为private的__clone()方法。单例类不会创建实例的副本,而是内部存储的实例返回一个引用。

举例:数据库连接职责的集中控制


class Db
{
   private $_db;   // 构建对象时其值被填充
  public static $_instance;   // 静态变量,用于保存类仅有的一个实例

  private function __construct()
  {
    $this->_db = pg_connnect('.....');
  }

  // 私有的__clone方法消除了PHP语言中可以复制对象,防止破坏单一职责
 private __clone() {}

  // 单例模式的实际构造
 public static function getInstance()
 {
    // 检查静态实例变量是否已保存Db类的一个实例
   if (! (self::$_instance instanceof self)) {
     self::$_instance = new self();
   }

   return self::$_instance;
 }

  public functon query($sql)
  {
     // 执行查询的code
  }
}

静态特性的“缺陷”

静态特性应用在变量,类和方法上,带来的好处是无可厚非的:

1)作为静态变量(用static关键字修饰的函数变量,创建时可提供默认的初始化值,其初始化值不能是表达式),在某一函数执行后,其值仍然不会丢失。这可在全局变量作为模拟静态时,消除了变量名称冲突的现象。

2)作为类的静态属性或方法,其变量值或方法对每个类的实例都是有效的,即是所有的实例共享这个成员或方法。这样可以避免创建类的实例,省略实例化的代码,更高效些(因为类的每个实例都会占用一小部分的系统资源)。

然后,静态类会对特定类型的测试产生影响。因为静态特性的使用会导致类之间通过名称绑定一起,使得单独的测试某个组件变得更加困难。因为类的名称是硬编码的,这样测试时,这个类不容易被模拟。

有 这么个设计原则,叫IoC(控制反转),它试图在OOP中去掉所以相互依赖的现象。相互依赖现象少了,越容易单独测试某个组件。也使对象具有更好的多态性和封装性。这是对于复杂的系统来说,是非常重要的。显然,使用静态特性,IoC就受到限制。

在PHP的应用中,静态特性还是很受欢迎的。现在,自己只能等着瞧它如何“走”~

简单思考

来个简单的滑动显示评价等级星标识。即是假如有个星标识,当鼠标移到到第三个星标识时,前三个星“满色”(充满颜色),后二个没有颜色。看了一些例子,有写得很棒的,很玄的。下边给个很简单的:

function stars(num)
{
	var items = document.getElementById('rating_stars').getElementsByTagName('a');

	if (items.length) {
		for (var i=0; i < items.length; i++) {
			items[i].className = i <= num ? '' : 'disabled';
		}
	}
}

对a标签的className定义下,就可以看到效果。 很简单也很不美观,但效率还是过得去吧,不过有时候觉得,想法简单点,可能会有好的局面和收获。

Ecshop的MySQL类autoReplace()方法重写

如题:

    // autoReplace
    public function autoReplace($table, $field_values, $update_values, $where = '')
    {
        $field_descs = $this->getAll('DESC ' . $table);

        // primary key | fields
        $primary_keys = $field_names = array();
        foreach ($field_descs as $value) {
            $field_names[] = $value['Field'];
            
            if ($value['Key'] == 'PRI')  {
                $primary_keys[] = $value['Field'];
            }
        }

        // action fields
        $fields = $values = array();
        foreach ($field_names as $value) {
            if (array_key_exists($value, $field_values) ) {
                $fields[] = $value;
                $values[] = "'" . $field_values[$value] . "'";
            }
        }

        // sets
        $sets = array();
        foreach ($update_values as $key => $value) {
            if (array_key_exists($key, $field_values) ) {
                if (is_int($value) || is_float($value)) {
                    $sets[] = $key . ' = ' . $key . ' + ' . $value;
                } else {
                    $sets[] = $key . " = '" . $value . "'";
                }
            }
        }
        
        // not exists primary key
        if (empty($primary_keys) && !empty($fields)) {
        	$sql = 'INSERT INTO ' . $table . '(' . implode(', ', $fields) . ') VALUES(' . implode(', ', $values) . ')';
        } else {
        	if ($this->version() >= 40102) {
        		$sql = 'INSERT INTO '. $table .'('. implode(',', $fields) .') VALUES('. implode(',', $values) .')';
        		
        		if (!empty($sets)) {
        			$sql .= ' ON DUPLICATE KEY UPDATE  '. implode(',', $sets);	
        		}
        	} else {
        		// where
        		if (empty($where)) {
        			$where = array();
        			
        			foreach ($primary_keys as $key) {
        				$where[] = $key = "'". $field_values[$key] ."'" ;
        			}
        			
        			$where = implode(' AND ', $where);
        		}
        		
        		if (!empty($where) && (!empty($sets) || !empty($fields))) {
        			if (intval($GLOBALS['db']->getOne("SELECT COUNT(*) FROM $table WHERE $where")) > 0) {
        				if (!empty($sets)) {
        					$sql = 'UPDATE '. $table .' SET '. implode(',', $sets) .' WHERE '. $where;
        				}
        			} else {
        				if (!empty($fields)) {
        					$sql = 'REPLACE INTO '. $table .'('. implode(',', $fields) .') VALUES('. implode(',', $values) .')';
        				}
        			} // else
        		}
        	} // else
        } // end if
        
        return empty($sql) ? false : $this->query($sql);
    }

已经重写过MySQL类(使用MySQLi方法),需要者Email:ljlwill@gmail.com。

同一台服务器使用IIS和Apache

让IIS和Apache共同使用80端口,访问时候不需要加端口号的方法:

一,多个IP的情况 (假设有192.168.0.1和192.168.0.2两个IP)

1)IIS默认的情况下会占用所有IP的80端口,在cmd输入此命令:netsh http add iplisten ipaddress=192.168.0.1,让IIS只监听192.168.0.1的IP (针对IIS7版本,对于较低版本的IIS可以搜索其绑定的方法)

2)Apache监听指定的IP,在httpd.conf设置Listen 192.168.0.2:80

二,一个IP的情况

使用Apache的代理功能(效率低),先设置Apache监听80端口,IIS监听其他端口,如8080,然后在Apache的httpd.conf取消以下注释:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_ftp_module modules/mod_proxy_ftp.so

然后建立一个虚拟主机,将该域名的所有访问转向8080端口。

<VirtualHost *:80>
ServerName www.test.com
ProxyPass / http://localhost:8080/
ProxyPassReverse / http://localhost:8080/
</VirtualHost>

地道的技术人

基本条件:

1)能解决技术问题

2)能承担技术责任

3)能融入技术团队

 

升级条件:

1)能提供技术方案 – “要解决客户需求问题”

2)能承担项目责任 – “技术你的能力,项目是你的责任”

3)能带领技术团队 – “不要一个人玩,要学会带着大家一起玩”

 

高级条件:

1)能指出技术方向 – “要超越企业内部需求,要超越客户外部需求”

2)能承担商业责任 – “ 尽管不是CEO,商业失败也会上门要债”

3)能打造并领导技术团队 – “不是个人,不是大锅饭,要的是团队”

PHP的扩展

http://tech.qq.com/a/20110512/000301.htm (对其进行一些概括)

1 什么是PHP扩展化?

PHP本身是用C语言写的,你所编写的PHP脚本到最后都是通过C代码执行的,这时候PHP还提供一种方式可以直接写用C来写一个共享库,动态的共享库, 把它加载到PHP中,通过这种方式让你业务模式以C模式存在在PHP当中,这个模式就叫扩展,PHP提供一个很强大模块来支持你自己PHP扩展。

2 什么样的情况下我们应该使用扩展?

两种方式是需要扩展,1)第一种方式我们有一些,比方说已经成熟的C库,我们PHP许多办法直接用,我必须用一个扩展把它桥接过来,这种情况下需要使用PHP扩展;2)还有一种情况我对CPU密集型的东西,比方说我有一个算法,或者我有一个很复杂,很复杂的加密算法。这个算法如果我用PHP写的话非常慢,对于这种CPU密集型的东西,我是可以把它扩展化用C来实现,这样的话能提高性能。

3 扩展为什么会快?

这里我要提一下FaceBook极致,去年11月份极致把一个应用性能提高到4倍,他是怎么做到的呢?我们大家听各种各样报告,是把PHP编译成C++, 他这个编译其实不是说我根据你的逻辑找到对应的C代码进行编辑,他做的更多是把这个符号解析给拿掉了。我们在PHP里面,我们的变量,我们的函数都是存储 在一个一个关联数字结构里面,他这个结构设计足够精妙,确实也花很大心思去设计,但是当我们使用一个变量,或者一个方式的时候,都需要从这个表里面去查 的,这个过程是非常耗时的。

所以,PHP性能绝大部分低也是这个关键。PHP就把能在编译期间确定的符号就把它直接替换掉,相当于我们C程序编译的时候把符号直接换成二进制地址的一 样,就是一个符号回天。这只是一方面,还有一方面为什么扩展会比PHP快?这个我们抛开一切问题,一切IO,抛开一切内存存储我们来算一个简单算术题,一 个1G赫兹CPU能编织多少,这也是PHP比较慢一个原因。

淘宝李晓拴:淘宝网PHP电子商务应用

大家好,大家知道淘宝搜索是一个典型PHP架构。在座同学不知道有多少人使用过淘宝搜索可以举手示意一下?在开始这个话题之前我们先谈一下Polyglot,多语言混合编程,淘宝有很多系统是利用Java来开发的。淘宝大概有上千名Java工程师,也做了非常多Java工作,我们为什么还要选择PHP呢?一方面来讲我们利用一些外部动态,PHP开发效率更高一些,另外我们技术熟练程度。

所以,也有应了那句话“没有最好,只有最适合”。其实我们发现不管是任何语言来讲,Java也好,PHP也好和C也好,我们会利用Java做一些接口,利用PHP来写一些VO的功能,同时也会利用C写一些PHP扩展。接下来进入我们今天第一个话题,搜索产品介绍。正如大家所看到一样,这个就是我们淘宝搜索首页,非常简单一个搜索框一个搜索按纽,下面是一些热门关健词,当你搜索之后就会进入我们搜索结果页,看上去有点复杂。只要大家用过淘宝,基本上大家都可能进入这样一个页面。

今天主要内容就是讲解这个页面,还有其他搜索产品,一淘,是淘宝面向站外用户推出电子商务搜索引擎,这是一淘的首页,和淘宝搜索其他产品一样,也是标准的LAMP架构。接下来一个产品是我们的排行榜,和前面有一些差别,使用NGS来做,排行榜有非常多静态内容,我们在服务器选题上采用了NGS。

接下来我来讲一讲淘宝搜索整体架构,讲架构之前,可能会讲一下共同模块,这样大家对理解架构更加容易。首先,看一下网络,这张图其实是一个非常简单的落说明图,用户访问搜索域名,会根据用户IP定位到不同机房,比如有电信机房,网通机房,和教育网。为什么有三个机房呢?第一是有中国特殊国情决定的,第二也是为了容灾,每个机房大概都有几十台机器。淘宝搜索每天流量也在每天几亿,我们每个机房部署几十台机器来控制流量。

这些机器主要来请求PHP,页面还有很多资源文件,像CSC,还有图片,这是部署在我们CDN上。每台机器普通64位Linux系统,安装是阿帕奇,没有用5.3,因为我们一些扩展可能也是历史原因,一些扩展目前还不能迁移到5.3上,这也是我们接下来要做的工作。这个是放大搜索结果页,大家可以看到图上整个页面大概分了几个区域。最上面是搜索框,比如说有LOGO,有搜索框,再下面有一些导航条,中间这个区域我们把它叫做智能导航模块,右侧叫做掌柜推荐,大家可以理解为广告。

这个其实是所有搜索结果页基本都比较类似的,不管大家用Google也好,百度也好,或者其他MSN都非常类似,上面搜索框做搜索结果,右侧是广告。因为淘宝搜索页大概有3到4屏,这页主要是搜索相关一些筛选功能,比如上面看到有不同的筛选区,下面有不同结果,只不过大家没有特别留意。上面那个区我们叫做产品搜索区域,中间这个区域叫做商品搜索区域,在往下是其他一些文字链之类,最下面是翻页。

有些细节大家可能不太好理解,打个比方。大家去Google搜索的时候,当你搜索北京天气的时候,或者搜索北京奥运会的时候,搜索结果页里面有非常多的内容,比如有新闻的,有网页,甚至还有论坛的。不同的来源,其实有不同的后端服务提供的,怎么样在搜索结果页里面把它展现出来,这其实就是一个问题,是我们问题和挑战。

这里面有两个核心问题,第一个来讲,对于查询应该展示哪些模块,并不是每次搜索的时候都要出产品搜索结果。第二个问题,如果你要查询多个模块的时候,怎么样保证高性能,让用户看到快速的搜索结果。试想一下,我们这个结果我们回到上一页,这个页面里面其实是要查非常多服务,中间智能导航就是有很多服务,下面有一个相关搜索模块,包括右侧广告,和下一页产品搜索结果,商品搜索结果,这样算下来这个页面要查5到6个业务模块。

所以,怎么样查询多个模块的时候,能够保持高的性能,让用户快速看到搜索结果。带着这些问题,我们来看一下它的架构。这张架构其实也是非常简单图,首先用户可以进入我们一些入口程序,相当于搜索一个非常简单的教研程序看看是不是合法和编码。这些工作做完之后,会把用户搜索词,用户搜索诺基亚,搜索凉鞋等等词分配到解析,对于搜索来说理解用户意图非常重要,可以知道用户想要找什么,要找的这个东西,应该在哪个内幕下,或者有哪些属性。

所以,我们会去调来这个模块获取用户相关信息,查取相关内容。我们知道核心一个问题,应该展示哪些模块,同时对这个结果,应该怎么去展示。在这个时候,我们会去请求缓存模块,因为像我们刚才讲的一样,他其实有5、6个后台请求模块,我们会去看看缓存有没有命中,如果命中缓存问题就非常简单,直接把这个数据从缓存拿出来一组装,通过这个模板来做一切展示。

如果没有命中缓存的话,我们会有一个专门检索模块,会根据你需要的数据,必须你需要六个模块,根据六个模块去请求后台一些服务,在这里面是并发来做的,如何实现会在接下来讲。所以,通过这样一个架构,能够实现查询内容,以及能够并发去获取相关数据。接下来,讲一下性能优化,因为对搜索来讲,性能方面是非常关注。

这张图是09年Google合并的一些相关研究人员,他们通过一些实验对比来发现,性能对于搜索影响。当这个网站速度下降几秒之后,他们会发现用户点击,用户收入,相关广告收入,用户的满意度都会有非常明显下降。其实,性能来讲,这个话题这几年也变的逐渐非常热门起来,不管是从06年雅虎提出一些相关APP Store,或者前端优化十几跳军规,一直到去年这里面领军人物史蒂夫提出一个WTO概念,做性能优化已经可以做成一个产业。

所以,更好性能意味着更低的成本,更高收入,以及更好的用户体验。所以,我们在性能优化方面也做了非常多的工作。这些工作整理起来,我们可以把它归纳为性能规划CPP的原则,第一条就是Cache,缓存。做这种网站缓存非常重要,甚至有时候是非常关键一点。缓存我们通常使用两个方式,第一就是APC,如果大家做PHP可能对APC都比较了解。第二点就是Memcached,我们使用了一些缓存。

再下面一点就是并发,PHP并没有多线程概念。这里面我们利用一些技术,可以并发去访问后台一些接口。第三点就是预期,其实做性能也好,类似于优化也好,有时候你不能达到足够快,至少也要让这个网站看上去比较快,我们把它叫做预期。其实简单做法,让这个网站能够支持这种类似于分化输出,当用户搜索完了之后,你可以让你的网站,搜索框先出来,再去查后台模块,最后在展示最下面的内容。

这张图就是关于Cache一个介绍,比如APC。提供一些APC类似于Opcode的软件也好,我们选择的是APC。从这张图上可以看到,使用APC以后,能够大幅优化PHP代码解释时间,我们之前也做过一些性能对比,用过APC之后能够把性能提升25%-40%左右。对于APC还有很深入话题,比如APC里面有一些参数,有兴趣的同学可以上网查一下,也可以跟我交流。

这张图是一个Memcached的事例,基本原理就是Check,Miss,Store,Check是第一步,当你最开始访问这个模块是没有,如果开始没有,就会去到后台计算相关数据,比如去查数据库,去请求远程接口。当拿到结果之后,会把这个数据存储到Cache里面去,并且把这个结果反映给输出,你第二次来的时候就命中Cache,所以Cache直接输出结果了。

对于Memcached来讲是一个非常成熟技术,我们用Memcached大概有几十台机器,每台机器开了几个G来做,大概有几百个G,是一个独立的Cache集群。因为我们是多个模块,当你去请求Cache,可以查一下有没有命中,如果没有命中可以请求后台,你可以一次性向Memcached发请求,一次性把请求发过去看有没有命中,他会告诉你有几个模块命中有几个模块没有命中,我们在去查后端,可以减少Memcached的请求。

下面和大家分享一下并发,PHP其实不支持多线程。如果说要串行访问多个后台缺口会非常严重影响性能,做一个简单计算,刚才所讲一样,如果有5个后台模块。每个模块访问需要50毫秒,5×5就是250毫秒,对做搜索来讲其实是非常慢了。所以,如果串行访问后台接口会非常慢。我们解决方案是使用CURL和Multi功能,下面列了一些相关函数,大家可以在下面去查查手册可以看一下,这里面还有更详细的一个文章来介绍。

基本原理利用提供封装功能一次性把请求发出去,等着响应拿到结果进行处理。按照优化原则,80%优化都集中在前端。前端我们做了一些尝试,比如我们了CDN Combo,合并HTTP请求。比如我们这个页面需要用5个页面表,我们可以对请求进行合并,减少请求优化性能。同时,按照最常规做法就是压缩CSS/JS文件,压缩完之后尺寸可以大大减少,提高加载速度。

还有一些做法,我们在首页预先加载结果页所需部分文件,用户要做的事情通常是搜索。在首页可以预先加载所需要的CSS和JS文件,在首页进行一个平衡,据我了解Google很多网站都是这么做的。还有图片的Lazy-Load,相当于业界一个标准做法,像搜索结果页大概有40张图片,大多数情况下来讲,用户可能只会看前面几张图片,如果满意就点走了,如果不满意会做一些搜索和其他筛选。接下来你屏幕以下非常多的图片就有点浪费了,如果用户不看的话,所以我们就采用Lazy-Load,当用户滚屏的时候可以大幅度提高性能,降低带宽。

接下来给大家讲一下监控报警,每天几百台机器监控报警也是非常重要的。这张图是截取在我们公司里面一个哈勃监控系统,我们通常统计了命中率,统计了一些数据在这里面进行分析,具体实现大概是这样的。我们的程序是有日志,比如有统计PV,超时数据,如果一旦访问后台结果超时的时候会在日志里面做记录。还有Latency,如果发现一些问题,我们定一些规则,比如超时时数达到多少,或者PV变化,就会发短信进行报警。

同时,我们刚才讲了我们有非常多的memcached-top来作为缓存,所以我们使用memcached脚本监控memcached集群运行情况。我们使用xhprof采样获得线上程序运行情况,有点类似与PHP。由于时间关系,今天我要讲的内容基本上就这些,其实淘宝搜索他的内容非常多,刚才也只是讲到一些点,感兴趣的同学可以会后跟我联系。我们也在招一些PHP工程师,我们也非常希望大家能够加入我们,和我们一起来做下一代的淘宝搜索。我的联系方式是jiansu@taobao.com,还可以访问我们的微博,感谢大家。

箴言

每一个令你真正动心的女孩,必有一点其他女孩不再会使你感觉到的极美之处,这一极美之处会在一个阶段里不由分说地主宰了你,令你全身心地感动。所以它应该是你终身的神祗,即便分手也不可以亵渎它。否则就是亵渎了你自己的感情。

如果你仅仅是对某个女孩感兴趣,却没有那种原子裂变似的反应,你不可去招惹人家。你可以等待,等待时间和机遇为你揭示这种兴趣的源头,爱与发现是紧紧相连的。

语言在真情的压迫下是没有表现能力的。甜言蜜语说得越顺嘴越有才气就越不可信任。但是人们往往迷恋语言带来的快感。你要慎用语言,有“涩”的感觉才是最高境界。

php基本安全

复习下PHP安全的基础知识:

1)关闭注册全局变量功能

即是在php.ini中register_globals=off(也是默认)。它决定是否将 EGPCS(Environment,GET,POST,Cookie,Server)变量注册为全局变量。 如果register_globals=on, 客户端提交的数据中含有GLOBALS变量名, 就会覆盖服务器上的$GLOBALS变量。

2)在使用变量之前,进行初始化。

3)过滤全部输入数据。

其方式主要取决于数据的类型。

4)防止SQL的注入。

应该使用特定语言的数据库转义函数。如mysql_real_escape_data()。

5)谨慎使用执行命令的函数。

如eval(), exec(), system(), passsthru(), popen(), 反撇号(`)。如果在命令中含有变量,无比进行安全检查。还应该再使用escapeshellarg()和escapeshellcom()进行预处理。

6)谨慎使用变量引用包含文件。

7)session会话安全。可改变其默认目录或利用数据库存储其数据。

8)重命名通过HTTP(浏览器)上传的文件。

9)隐藏站点错误消息,把错误报告级别设置为最高。

10)在显示提交的数据时,过滤其HTML和Javascript(如可用strip_tags()函数)

等等