定义一个类只有一个实例,并提供一个全局访问点。在应用场景中,多人协作开发。调用mysql类的实例对象,应该避免new操作消耗大量的资源数据库链接,消耗大量资源。现在网站有两个瓶颈,第一个是带宽,第二个是数据库。数据库连接原理,连接不上怎么限制,让多人开发,怎么操作都只能获取一个对象?解决方法:使用单例模式保证一个类只有一个实例。主要思想是保护或者私有构造Function,防止外部实例化,内部开放一个publicstatic方法。它负责检测实例化类中是否存在静态私有属性来存储对象。如果静态私有属性已经存储了对象,则直接返回该属性。实现过程第一步:一个普通类这个普通类可以用new来实例化,显然不是单例方案:别让newclassSingle{function__construct(){}}$s1=newSingle();$s2=newSingle();第二步:既然实例化多个对象是new,那么就不要让new来保护/私有构造方法。确实外面不能用new,但是拿不到对象。这不叫单例,而是零例。:写一个静态方法,在方法内部新建一个对象,调用类内部的构造方法classSingle{privatefunction__construct(){}}$s1=newSingle();//报错privatemethodscannotbecalledoutsidetheclass{}三步:写一个方法,通过静态方法在类内部的new对象中获取一个实例对象。返回对象如何调用这个方法只能设置为staticpublic(因为需要在类外静态调用)。虽然是从内部new的,但是还是会生成不同的对象,怎么控制只能new一次解决思路:写一个静态属性实例,将内部new的对象返回给实例并保存,然后做一个判断类单{public$hash;//随机代码私有函数__construct(){$this->hash=mt_rand(1,99999);}staticpublicfunctiongetInstance(){returnnewSingle();}}$s1=Single::getInstance();$s2=Single::getInstance();print_r($s1);//[哈希]=>12904print_r($s2);//[哈希]=>99339if($s1===$s2){echo'是同一个对象';}else{echo'不是一个对象';//isnotanobject}第四步:在类内部使用静态方法创建新的对象,并将对象保存到类内部的静态属性中写一个静态私有属性,将对象保存到这个属性中(只能静态调用,并防止属性在类外被修改)判断$instance是否为Single类的实例,控制是否为新对象,确保目前只有一个对象:一个简单的单例就绪classSingle{公共$哈希;//随机代码staticprivate$instance=null;私有函数__construct(){$this->hash=mt_rand(1,99999);}staticpublicfunctiongetInstance(){if(!self::$instanceinstanceofSingle){//instanceof判断一个对象是否是类的实例self::$instance=newSingle();}返回自我::$实例;}}$s1=Single::getInstance();$s2=Single::getInstance();print_r($s1);//[哈希]=>6556print_r($s2);//[hash]=>6556if($s1===$s2){echo'是同一个对象';//是同一个对象}else{echo'isnotanobject';}漏洞一:单实例的硬写继承是行不通的。继承,然后写一个同名的公共构造函数,就可以创建一个新的对象。父类的构造函数是私有的,父类的标志,不是不能重写吗?子类中的publicfunction__construct()是不重写的,子类没有重写的权限。这是一个同名的构造函数。解决方法:用final修饰父类的构造函数。可以继承但不能重写classSingle{公共$散列;//随机代码staticprivate$instance=null;私有函数__construct(){$this->hash=mt_rand(1,99999);}staticpublicfunctiongetInstance(){if(!self::$instanceinstanceofSingle){//instanceof判断一个对象是否是某个类的实例self::$instance=newSingle();}返回自我::$实例;}}$s1=Single::getInstance();$s2=Single::getInstance();print_r($s1);//[哈希]=>43232print_r($s2);//[hash]=>43232classTestextendsSingle{publicfunction__construct(){}}$t1=newTest();$t2=newTest();print_r($t1);//[哈希]=>6524print_r($t2);//[hash]=>4332方案一:使用final修饰父类结构函数可以继承不能被重写final关键字修饰的类方法可以被继承不能重写s1s2t1t2指向同一个对象echo'
';classSingle{public$hash;//随机代码staticprivate$instance=null;最终私有函数__construct(){$this->hash=mt_rand(1,99999);}staticpublicfunctiongetInstance(){if(!self::$instanceinstanceofSingle){//instanceof判断一个对象是否是某个类的实例self::$instance=newSingle();}返回自我::$实例;}}$s1=Single::getInstance();$s2=Single::getInstance();print_r($s1);//[哈希]=>24726print_r($s2);//[hash]=>24726classTestextendsSingle{}$t1=Single::getInstance();$t2=Single::getInstance();print_r($t1);//[哈希]=>24726print_r($t2);//[hash]=>24726漏洞二:问题一bug解决了吗?然后我clone$t3=clone$t2,将$t2对象完整复制一份,存放在新开的内存中。s1s2t1t2指向同一个对象,t3指向新复制的对象。不是有两个对象吗??echo'';classSingle{public$hash;//随机代码staticprivate$instance=null;最终私有函数__construct(){$this->hash=mt_rand(1,99999);}staticpublicfunctiongetInstance(){if(!self::$instanceinstanceofSingle){//instanceof判断一个对象是否是某个类的实例self::$instance=newSingle();}返回自我::$实例;}}$s1=Single::getInstance();$s2=Single::getInstance();print_r($s1);//[哈希]=>24726print_r($s2);//[hash]=>24726classTestextendsSingle{}$t1=Single::getInstance();$t2=Single::getInstance();print_r($t1);//[哈希]=>24726print_r($t2);//[哈希]=>24726$t3=克隆$t2;print_r($t3);//[hash]=>24726这里[hash]之所以有相同的值是因为在clone$t3对象的时候[hash]已经有值了if($t3===$t2){echo'是同一个对象';}else{echo'不是对象';//notanobject}解决方案二:防止使用clone克隆对象在单例模式的最终版本中使用魔术方法函数__clone(){}来防止使用clone克隆使用self而不是类名的对象//finalversionofthesingletonmodeclassSingle{//创建一个静态私有变量来保存这个类的对象staticprivate$instance=null;//防止使用new直接创建对象finalprivatefunction__construct(){}staticpublicfunctiongetInstance(){//判断$instance是否为Singleton对象,如果不是则创建if(!self::$instanceinstanceofself){//instanceof判断一个对象是否是某个类的实例self::$instance=newself();//推荐使用self代替类名}returnself::$instance;}//防止使用clone克隆对象作为private私有函数__clone(){}}$s1=Single::getInstance();$s2=Single::getInstance();单例实践单例数据库类DaoMysql{private$mysql_link;私有静态$instance=null;最终私有函数__construct($host,$user,$pwd){$this->mysql_link=mysqli_connect($host,$user,$pwd);}publicstaticfunctiongetInstance($host,$user,$pwd){if(!self::$instanceofself){self::$instance=newself($host,$user,$pwd);}返回自我::$实例;私有函数__clone(){}}$d1=DaoMysql::getInstance('localhost','root','');$d2=DaoMysql::getInstance('localhost','root','');var_dump($d1,$d2);内存图分析$instance和p1p2指向同一个对象标识符