第十四章:面向对象
1:面向对象的基本概念
这里指的面向对象,准确的说应该叫面向对象编程(OOP),是面向对象的一部分。面向对象包括3部分:面向对象分析(Object Oriented Analysis,OOA)、面向对象设计(Object Oriented Design,OOD)以及面向对象编程(Object Oriented Programming,OOP)
1.1-1.2:类、对象
1.3:OOP三大特点:封装性、继承性、多态性
2:PHP与对象
2.1:类的定义—— class
class SportObject {....}
2.2:成员方法——类中的函数被称为成员方法。区别:函数实现的是某个独立的功能,而成员方法是实现类的一个行为,是类的一部分
2.3:类的实例化—— 实例化通过new关键字类声明一个对象。然后使用 "对象名 -> 成员方法" 的格式来调用要使用的方法
2.4:成员变量——类中的变量(也有称为属性或字段的)。用来保存信息数据,或与成员方法进行交互来实现某项功能: "对象名 -> 成员变量"
注意:无论是使用 "$this-> " 还是使用 "对象名 -> " 的格式,后面的变量是没有$符合的。
2.5:类常量—— const关键字声明
2.6:构造方法和析构方法
1)构造方法——当一个类实例化一个对象时,可能会随着对象初始化一些成员变量。构造方法是生成对象时自动执行的成员方法,作用就是初始化对象。格式如下:
void __construct ([mixed args [,...]] ) //注意是两条下划线
2)析构方法——与构造方法相反,是对象被销毁时被调用的,作用是释放内存
void __destruct (void)
说明:PHP使用的是一种”垃圾回收“机制,自动清除不再使用的对象,释放内存。
2.7:继承和多态的实现——根本的作用就是完成代码的重用
1)继承—— extends 关键字声明
2)多态——同一种方法,产生了不同的形态。多态存在两种形式:覆盖和重载
A:覆盖——在子类中重写父类的方法
B:重载——函数重载是指一个标识符被用作多个函数名,且能通过函数的参数的个数或参数类型将这些同名的函数区分开来
2.8: ”$this ->“ 和 ”::“ 的使用
1)$this -> 调用成员方法:”对象名 -> 方法名“,但在定义类时,根本无法得知对象的名称是什么。此时要想调用类中的方法,就要用伪变量 $this -> 。$this的意思就是本身,所以它只能在类的内部使用
注意:get_class()函数返回对象所属类的名字,如果不是对象,则返回false
2)操作符”::“——相比伪变量$this 只能在类的内部使用,操作符”::“更为强大,操作符"::"可以在没有声明任何实例的情况下访问类中的成员方法或成员变量。通用格式:
关键字::变量名/常量名/方法名
这里关键字分为3种情况:
parent关键字:可以调用父类中的成员变量、成员方法和常量
self 关键字:可以调用当前类中的静态成员和常量
类名:可以调用本类中的变量、常量和方法
2.9:数据隐藏——面向对象编程的特点之一就是封装性,即数据隐藏。注意,对于成员方法,如果没写关键字,默认是public
关键字:public、private、protected、static和final
2.10:静态变量(方法)—— 不是所有的变量(方法)都要通过创建对象来调用。可以通过给变量(方法)加上 static 关键字类直接调用:
关键字::静态成员
关键字可以是self,或者静态成员所在的类名
说明:静态成员不用实例化对象,当类第一次加载时就已经分配了内存空间,所以直接调用静态成员的速度要快一些。但如果静态成员声明过多,空间一直被占用,反而会影响系统的功能。
3:PHP对象的高级应用
3.1:final关键字——被final修饰过的类不能被继承,也不能有子类。被final修饰过的方法不可以进行重写,也不可以被覆盖
3.2:抽象类—— abstract 关键字声明,格式: abstract class ClassName{...}
抽象类与普通类的区别:抽象类至少要包含一个抽象方法。抽象方法没有方法体,其功能的实现只能在子类中完成。abstract function functionName();
3.3:接口的使用——PHP只支持单继承。要想实现多重继承,就要使用接口。使用关键字 interface。子类通过implements来实现接口
3.4:克隆对象
1)克隆对象—— clone 关键字
$obj1 = new ClassName();
$obj2 = clone $obj1;
2)__clone() 方法——有时除了单纯的克隆对象外,还需要克隆出来的对象可以拥有自己的属性和行为。__clone()方法的作用是:在克隆对象的过程中,调用__clone()方法,可以使克隆出来的对象保持自己的一些行为及属性
class SportObject{ //声明私有变量,并赋初值为 book private $object_type = 'book'; //声明成员方法setType,为变量$object_type 赋值 public function setType($type){ $this->object_type = $type; } //声明成员方法getType,返回变量$object_type 的值 public function getType(){ return $this->object_type; } //声明__clone()方法,修改变量$object_type的值 public function __clone(){ $this -> object_type = "computer"; } } //实例化对象$book1 $book1 = new SportObject(); //使用普通数据类型的方法给对象$book2 赋值 $book2 = clone $book1; echo '对象$book1 的变量值为:'.$book1->getType(); echo '<br>'; echo '对象$book2 的变量值为:'.$book2->getType();
3.5:对象比较——判断两个对象之间的关系是克隆还是引用,可以使用比较运算符"=="和"==="。两个等号是比较两个对象的内容,3个等号是比较对象的引用。
3.6:对象类型检测—— instanceof 操作符可以检测当前对象是属于哪个类
ObjectName instanceof ClassName。如:if ($cBook instanceof MyBook)
3.7:魔术方法——以两个下划线开头的方法。注意php中保留了所有以"__"开头的方法,所以不要创建这样的方法
1)__set()和__get()方法:
当程序试图写入一个不存在或不可见的成员变量时,PHP就会执行__set()方法。方法包含两个参数,分别表示变量名称和变量值,不可省略
当程序调用一个未定义或不可见得成员变量时,可以通过__get()方法来读取变量值,它有一个参数,表示要调用的变量名
注意:如果希望PHP调用这些魔术方法,首先必须在类中进行定义。否则PHP不会执行未创建的魔术方法
class SportObject{ //私有变量type private $type = ''; //声明魔术方法__get() private function __get($name){ //判断变量是否被声明 if (isset ($this->$name)){ echo '变量'.$name.'的值为:'.$this->$name.'<br>'; }else{ echo '变量'.$name.'未定义,初始化为 0 <br>'; //如果未被声明,则对变量初始化 $this->$name = 0; } } private function __set($name, $value){ if (isset($this->$name)){ $this->$name = $value; echo '变量'.$name.'赋值为:'.$value.'<br>'; }else{ $this->$name = $value; echo '变量'.$name.'被初始化为:'.$value.'<br>'; } } } $MyComputer = new SportObject(); $MyComputer -> type = 'DIY'; //给变量赋值 $MyComputer -> type; // 调用变量$type $MyComputer -> name; //调用变量$name
2)__call()方法——当程序试图调用不存在或不可见的成员方法时,PHP会先调用__call() 方法来存储方法名及参数。包含两个参数:方法名和方法参数。方法参数以数组方式存在
class SportObject{ public function myDream(){ echo '调用的方法存在,直接执行此方法。'; } public function __call ($method, $parameter ){ echo '如果方法不存在,则执行__call()方法。<br>'; echo '方法名为:'.$method.'<br>'; echo '参数有:'; var_dump($parameter);//$parameter是一个数组 } }
3)__sleep()和__wakeup()方法——使用 serialize()函数可以实现序列化对象。就是将对象中的变量全部保存下来,对象中的类则只保存类名。在使用serialize()函数时,如果实例化的对象包含__sleep()方法,则会先执行__sleep()方法。该方法可以清除对象并返回一个该对象中所有变量的数组。使用__sleep()方法的目的是关闭对象可能具有的数据库连接等类似的善后工作
unserialize()函数可以重新还原一个被 serialize() 函数序列化的对象, __wakeup()方法则是恢复在序列化中可能丢失的数据库连接及相关工作
class SportObject{ private $type = "DIY"; //声明getType()方法,用来调用私有变量$type public function getType(){ return $this -> type; } public function __sleep(){ echo '使用serialize()函数将对象保存起来,可以存放到文本文件、数据库等地方<br>'; return $this; } public function __wakeup(){ echo '当需要该数据时,使用 unserialize() 函数对已序列化的字符串进行操作,将其转换回对象<br>'; } } $myBook = new SportObject(); $i = serialize($myBook); //序列化对象 echo '序列化后的字符串:'.$i.'<br>'; $reBook = unserialize($i); // 将字符串$i 重新转换为对象$reBook echo '还原后的成员变量:'.$reBook -> getType();
4)__toString()方法——当使用 echo 或 print 输出对象时,将对象转化为字符串
class SportObject{ private $type = "DIY"; public function __toString(){ return $this->type; } } $myComputer = new SportObject(); echo '对象$myComputer 的值为:'; echo $myComputer; /* 注意:如要没有 __toString() 方法,会发生致命错误(fatal error) echo或print 函数后面要直接跟输出对象 */
5)__autoload()方法——将一个独立、完整的类保存到一个PHP页中,并且文件名和类名保持一致。
如果要在一个页面中引进很多类,需要使用 include_once()函数或require_once()函数一个个引入,PHP5解决了这个问题,__autoload()方法可以自动实例化需要使用的类。当程序要用到一个类,但该类还没有被实例化,PHP5将使用__autoload()方法,在指定的路径下自动查找和该类名称相同的文件。如果找到,程序继续执行,否则报告错误。
//类文件SportObject.class.php 代码 class SportObject{ private $cont; public function __construct($cont){ $this -> cont = $cont; } public function __toString(){ return $this -> cont; } } //index.php文件的代码: function __autoload($class_name){ //类文件路径 $class_path = $class_name.'.class.php'; //判断类文件是否存在 if (file_exists($class_path)){ //动态包含类文件 include_once($class_path); }else{ echo '类路径错误'; } } $myBook = new SportObject("江山代有人才出 各领风骚数百年");
4:面向对象的应用——中文字符串的截取类
class MsubStr { function csubstr ($str, $start, $len){ //字符串的总长度 $strlen = $start + $len; //for循环,读取字符串 for ($i = 0; $i < $strlen; $i++){ //如果字符串中首个字节的ASCII序数值大于0xa0,则表示为汉字 //ord()函数返回ASCII值 if( ord ( substr ($str, $i, 1)) > 0xa0){ //每次取出两位字符(一个汉字)赋给变量 $tmpstr .= substr ($str, $i, 2); $i ++; }else{ $tmpstr .= substr ($str, $i, 1); } } return $tmpstr; } }
第十五章:PHP加密技术
1:php加密函数—— crypt()、md5()、sha1(),还有加密扩展库 Mcrypt 和 Mass
1)使用 crypt() 函数进行加密
string crypt(string str [, string salt]);
str是要加密的字符串,salt是加密时使用的干扰串。如果省略第二个参数,则会随机生成一个干扰串。crypt()函数支持4中算法和长度:
说明:默认情况,PHP使用一个或两个字符的DES干扰串,如果系统使用的是MD5,则会使用12个字符。可以通过CRYPT_SALT_LENGTH变量来查看干扰串的长度
crypt()函数是单向加密的,密文不可能还原成明码,而每次加密后的数据还不相同,如何对加密后的数据进行判断比较?这就是salt要解决的问题。crypt()函数用salt参数对明文进行加密,判断时,对输出的信息再次使用相同的salt加密,对比两次加密后的结果来进行判断
2)使用md5()函数进行加密——使用MD5算法(Message-Digest Algorithm 5:信息-摘要算法),它的作用是把不同长度的数据信息经过一系列的算法计算成一个128位的数值,就是把任意长度的字节串变换成一定长的大整数。注意这里是”字节串“而不是”字符串“,因为这种变换只与字节的值有关,与字符集或编码方式无关
string md5 ( string str [, bool raw_output] );
str是要加密的明文,raw_output参数如果设置为true,则函数返回一个二进制形式的密文,该参数默认为false
class chkinput{ var $name; var $pwd; function chkinput($x, $y){ $this->name = $x; $this->pwd = $y; } //定义方法,完成用户注册 function checkinput(){ include "conn/conn.php"; $info = mysql_query("insert into tb_user(user,password)value(' ".$this->name." ', ' ".$this->pwd. ' " )" ); if ($info == false){ echo "<script language='javascript'>alert('会员注册失败'); history.back();</script>"; /*exit()函数输出一条信息并退出当前脚本。是die()函数的别名*/ exit(); }else{ $_SESSION[admin_name] = $this->name; echo "<script language='javascript'>alert('注册成功');window.location.href='index.php';</script>"; } } } $obj = new chkinput ( trim ($_POST[name], trim (md5($_POST[pwd]))); $obj->checkinput();
3)使用sha1()函数进行加密——SHA算法和md5算法类似。SHA:Secure Hash Algorithm(安全哈希算法)
string sha1 ( string str [,bool raw_output] )
函数返回一个40位的十六进制数,如果参数raw_output 为true,则返回一个20位的二进制数。默认为false
2:PHP加密扩展库——Mcrypt扩展库可以实现加密解密功能。Mhash库则包含了MD5在内的多种hash算法实现的混编函数
注意:这两个在PHP最新版本已经默认安装和开启!
安装:PHP的主目录下包含了 libmcrypt.dll 和 libmhash.dll 文件,首先将文件复制到系统目录 windows\system32下,然后在php.ini中找到”;extension=php_mcrypt.dll“ 和 ”;extension=php_mhash.dll“,取消注释,重启服务器
1)Mcrypt 库常量——支持20多种加密算法和8种加密模式,分别通过函数mcrypt_list_algorithms() 和 mcrypt_list_modes() 查看。这些算法和模式在实际应用中要用常量来表示,写的时候分别要加上前缀:MCRYPT_ 和 MCRPT_MODED_来表示
//使用Mcrypt进行加密解密不像使用MD5、sha1 等函数,直接调用即可 $str = "被加密的内容"; $key = "key:111"; //密钥 $cipher = MCRYPT_DES; //密码类型 $modes = MCRYPT_MODE_ECB; //密码模式 //初始化向量 $iv = mcrypt_create_iv ( mcrypt_get_iv_size($cipher, $modes), MCRYPT_RAND); echo "加密前:".$str."<br>"; //加密。mcrypt_encrypt是加密函数 $str_encrypt = mcrypt_encrypt ($cipher, $key, $str, $modes, $iv); echo "加密后:".$str_encrypt."<p>"; $str_decrypt = mcrypt_decrypt ($cipher, $key, $str_encrypt, $modes, $iv); echo "还原:".$str_decrypt."<p>";
(1)加密函数:string mcrypt_create_iv ( int size [, int source] );
使用Mcrypt 进行数据加密、解密之前,首先要创建一个初始化向量(iv)。创建初始化向量需要两个参数:size指定了iv的大小,source为iv的源。source可以取如下值:
MCRYPT_RAND:系统随机数
MCRYPT_DEV_RANDOM:读取目录/dev/random 中的数据(UNIX系统)
MCRYPT_DEV_URANDOM:读取目录/dev/urandom 中的数据(UNIX系统)
(2)int mcrypt_get_iv_size ( string cipher, string mode):该函数返回初始化向量(iv)的大小。cipher:加密算法;mode:算法模式
(3)string mcrypt_encrypt ( string cipher, string key, string data, string mode [,string iv]) : data为要加密的数据
(4)string mcrypt_decrypt ( string cipher, string key, string data, string mode [, string iv] ) :解密函数
2)Mhash扩展库——支持MD5、SHA、CRC32等多种散列算法。使用mhash_count()和 mhash_get_hash_name() 函数输出支持的算法名称
$num = mhash_count(); //函数返回最大的 hash id echo "Mhash 库支持的算法有:"; for ($i=0; $i <= $num; $i++) { //输出每一个hash id的名称 echo $i."=>".mhash_get_hash_name($i)." "; }
如果在实际应用中使用上面的常量,需要在算法名称前面加上MHASH_前缀。
相比Mcrypt库的30多个函数,Mhash库只有5个函数,除了上面使用到的两个函数外,下面介绍其他3个函数:
(1)mhash_get_block_size() 函数: int mhash_get_block_size( int hash )
该函数用来获取hash的区块大小,如mhash_get_block_size(MHASH_CRC32)
(2)mhash()函数: string mhash ( int hash, string data [, string key] )
该函数返回一个哈希值,参数hash为要使用的算法,data是要加密的数据,key是加密密钥
(3)mhash_keygen_s2k()函数: string mhash_keygen_s2k ( int hash, string password, string salt, int bytes)
该函数将根据password和salt返回一个长度为字节的key值,hash是使用的算法。其中salt固定8字节,如果给出的值小于8字节,将用0补齐
$filename = '08.txt'; $str = file_get_contents($filename); $hash = 2; //设置hash值 $password = '111'; $salt = '1234'; $key = mhash_keygen_s2k (1, $password, $salt, 10);//生成key //使用$key、$hash对字串$str加密 $str_mhash = bin2hex(mhash($hash,$str,$key)); echo "文件08.txt的校检码是:".$str_mhash;