1、第5章 PHP高级编程,5.1 PHP函数,5.2 PHP面向对象程序设计,5.1 PHP函数,5.1.1 用户自定义函数 PHP为用户提供了自定义函数的功能,编写的方法非常简单,定义函数的格式如下: function function_name($parameter, ) /函数代码段 定义函数的关键字为function。function_name是用户自定义的函数名,通常这个函数名可以是以字母或下划线开头后面跟0个或多个字母、下划线和数字的字符串,且不区分大小写,需要注意的是,函数名不能与系统函数或用户已经定义的函数重名。,5.1.1 用户自定义函,在函数定义时,花括号内的代码就是在调用函
2、数时将会执行的代码,这段代码可以包括变量、表达式、流程控制语句,甚至是其他的函数或类定义。 例如: $b)echo “ab“;elseecho “a,5.1.2 参数的传递,如果希望函数修改外部传来的参数值,可以使用引用参数传递,只要在定义函数时在参数前面加上“ ?,5.1.3 函数变量的作用域,变量的作用域问题在第3章已经介绍过,这里再简要补充一下。由第3章的内容可知,在主程序定义的变量和在函数中定义的变量都是局部变量。在函数中定义的变量只能在函数内部使用。在主程序中定义的变量只能在主程序中使用,而不能在函数中使用。例如: ,5.1.4 函数的返回值,函数声明时,在函数代码中使用return
3、语句可以立即结束函数的运行,程序返回到调用该函数的下一条语句。例如: ,5.1.4 函数的返回值,中断函数执行并不是return语句最常用的功能,许多函数使用return语句返回一个值来与调用它们的代码进行交互。函数的返回值可以是任何类型的值,包括列表和对象。例如: =$b) /如果$a=$b则返回$areturn $a;else /如果$a,5.1.5 函数的调用,函数在声明后就可以被调用,前面的内容中已经接触过函数的调用了。例如,在打开一个文件时就需要调用系统函数fopen(),要包含一个文件时需要调用include()函数。 函数在调用时需要提供有效的参数,fopen()函数的语法格式如
4、下: resource fopen(string $filename , string $mode , bool $use_include_path , resource $context ) resource表示函数将返回一个资源变量,在函数的括号内给出了4个参数,方括号中的参数表示是可选参数,如果忽略可选参数则使用它们的默认值,但如果一个函数有多个可选参数,则必须按照从左到右的顺序使用默认值。例如,使用fopen()函数可以不给出$context参数,或者可以不提供$use_include_path和$context参数,但不能不提供$use_include_path参数而提供$conte
5、xt参数。下面的调用是有效的: fopen(“file.txt“, “r“);,5.1.5 函数的调用,另外,函数如果没有返回值,调用时使用函数名即可。如果函数具有返回值,则可以将函数的返回值赋给一个变量。例如: $array$j)$tmp=$array$j;$array$j=$array$i;$array$i=$tmp;return $array; $arr=array(6,4,7,5,9,2); /未排序的数组 $sort_arr=my_sort($arr); /将排序后的数组赋给$sort_arr foreach($sort_arr as $num)echo $num; /输出24567
6、9 ?,5.1.6 递归函数,PHP支持递归函数,递归函数就是调用函数本身,可以实现循环的作用。例如: ,5.1.7 变量函数,PHP中有变量函数这个概念,在变量的后面加上一对小括号就构成了一个变量函数。例如: $count(); 如果创建了变量函数,PHP脚本运行时将寻找与变量名相同的函数,如果函数存在,则尝试执行该函数,如果不存在则产生一个错误。为了防止这类错误,可以在调用变量函数之前使用PHP的function_exists()函数来判断该变量函数是否存在。例如: ,5.1.8 系统函数,用户自定义函数可以进行逻辑运算,而大部分的系统底层工作需要由系统函数来完成。 系统函数在本书前面的内
7、容中已经涉及多次,PHP提供了丰富的系统函数供用户调用,包括文件系统函数、数组函数、字符串函数等。通过使用这些函数可以用很简单的代码完成比较复杂的工作。但并不是所有的系统函数都能直接调用的,有一些扩展的系统函数需要安装扩展库之后才能调用,例如,有些图像函数需要在安装GD库之后才能使用。当前运行环境支持的函数列表可以在phpinfo页面查看。,5.1.9 实例设计一个计算器程序,【例5.1】 设计一个计算器程序,实现简单的加、减、乘、除运算。 新建EX5_1.php文件,输入以下代码。 运行文件后计算240*30的结果,如图5.1所示。,图5.1 计算器程序,5.2 PHP面向对象程序设计,5.
8、2.1 面向对象程序设计概念 面向对象的程序设计有三个主要特征:封装、继承和多态。 1. 封装 封装是将数据和代码捆绑到一起,避免外界的干扰和不确定性。在PHP中,封装是通过类来实现的。类是抽象数据类型的实现,一个类的所有对象都具有相同的数据结构,并且共享相同的实现操作的代码,而各个对象又有着各自不同的状态,即私有的存储。因此,类是所有对象的共同的行为和不同状态的结合体。 由一个特定的类所创建的对象称为这个类的实例,因此类是对象的抽象及描述,它是具有共同行为的若干对象的统一描述体。类中还包含生成对象的具体方法。 2. 继承 类提供了创建新类的一种方法,再借助于“继承”这一重要机制扩充了类的定义
9、,实现了面向对象的优越性。 继承提供了创建新类的方法,这种方法就是,一个新类可以通过对已有的类进行修改或扩充来满足新类的需求。新类共享已有类的行为,而自己还具有修改的或额外添加的行为。因此,可以说继承的本质特征是行为共享。 从一个类继承定义的新类,将继承已有类的所有方法和属性,并且可以添加所需要的新的方法和属性。新类被称为已有类的子类,已有类称为父类,又叫基类。,5.2.1 面向对象程序设计概念,3. 多态 不同的类对于不同的操作具有不同的行为,称为多态。多态机制使具有不同的内部结构的对象可以共享相同的外部接口,通过这种方式减少代码的复杂度。,5.2.2 在PHP中创建类、属性和方法,类是面向
10、对象程序设计的核心,它是一种数据类型。类由变量和函数组成,在类里面,变量称为属性或成员变量,函数称为方法。定义类的语法格式如下: class classname var $property= value;function functionname($args)/代码 ,5.2.2 在PHP中创建类、属性和方法,在类中,使用关键字var来声明变量,即类的属性。使用关键字function来定义函数,即类的方法。例如,以下是一个简单的类定义: class a var $a;function fun($b)echo “hello world“; 不能将类的定义放到多个文件或多个PHP块中,例如,以下的
11、做法是错误的:var $tmp; ,5.2.3 类的实例化与访问,在声明一个类后,类只存在于文件中,程序不能直接调用。需要创建一个对象后程序才能使用,创建一个类对象的过程叫做类的实例化。类的实例化使用new关键字,关键字后面需要指定实例化的类名,例如,定义一个Ctest类并实例化: stunumber=$str; /使用$this指针类内部的属性echo $this-stunumber; $obj=new Ctest; /创建Ctest类的一个对象$obj ?,5.2.3 类的实例化与访问,在实例化一个类时,有些类允许在实例化时接收参数,如果能够接收参数,可以使用以下代码创建对象,其中$arg
12、s是所带参数: $obj=new Ctest($args,); 在对象被创建之后,可以在类的外部对该对象的属性和方法进行访问,访问的方法是在对象后面使用“-”符号加上要访问的属性和方法。例如,创建了对象“$obj”,类中有属性“$stunumber”,要访问该属性可以使用“$obj-stunumber”,注意属性的前面没有“$”。 例如,访问Ctest类的属性和方法: $obj-stunumber=081102; /给对象$obj的属性$stunumber赋值 echo $obj-stunumber; /输出081102 $obj-add(081101); /输出081101,5.2.4 类的
13、访问控制,在PHP 4中,类的属性必须使用关键字var来声明,而在PHP 5中,引入了访问修饰符public、private和protected。它们可以控制属性和方法的可见性,通常放置在属性和方法的声明之前。 public。声明为公用的属性和方法,可以在类的外部或内部进行访问。public是默认选项,如果没有为一个属性或方法指定修饰符,那么它将是public的。 private。声明为私有的属性和方法,只可以在类的内部进行访问。私有的属性和方法将不会被继承。 protected。声明为被保护的属性和方法,只可以在类的内部和子类的内部进行访问。,5.2.4 类的访问控制,例如: number=
14、“081101“; echo $object-number; /输出“081101“ $object-Stuinfo(); /输出“学生信息“ $object-phone=“84565879“; /本语句出错,访问权限不够 ?,5.2.5 静态属性和方法,访问静态属性和方法时需要使用到范围解析符“:”,格式如下: classname:$attribute; /访问静态属性 classname:Cfunction($args,); /访问静态方法 例如: ,5.2.6 构造函数和析构函数,构造函数。在PHP 4中,在类的内部与类同名的函数都被认为是构造函数,在创建类的对象时被自动执行。而在PHP
15、 5中,构造函数的名称为_construct,“construct”的前面是两根下划线。如果一个类同时拥有_construct构造函数和与类名相同的函数,PHP 5将把_construct看做是构造函数。PHP中的构造函数可以带参数,也可以不带参数。 析构函数。类的析构函数的名称是_destruct,如果在类中声明了_destruct函数,PHP将在对象被销毁前调用析构函数将对象从内存中销毁,节省服务器资源。,5.2.6 构造函数和析构函数,例如: ,5.2.7 类的继承,1. 子类访问父类 在PHP中,允许通过继承其他类的方法来调用这些类里已经定义好的属性和方法,PHP不支持多继承,所以一个
16、子类只能继承一个父类。可以使用extends关键字来指明类与类之间的继承关系。例如,以下是类B继承类A的代码。 另外值得注意的是,继承是单方向的,子类可以从父类中继承特性,但父类却无法从子类中继承特性。 如果子类中没有自己的构造函数,那么子类在实例化时会自动调用父类的构造函数。如果子类中有自己的构造函数,则执行自己的构造函数。 如果要在子类中调用父类的方法,除了使用“$this-”外,还可以使用parent关键字加范围解析符,如“parent:functionname()”。建议使用后一种方法,因为前面的方法容易造成子类和父类方法结构不清。而对于父类的属性,在子类中只能使用“$this-”来访
17、问,属性是不区分父类和子类的。 继承可以是多重的,例如,类B继承了类A,类C继承了类B,那么类C也就继承了类B和类B的父类的所有特性。,5.2.7 类的继承,2. 重载 方法的重载是指在一个类中可以定义多个拥有相同名称的方法,通过参数个数和类型来区分这些方法,而PHP目前并不支持这一特性。但可以通过类的继承,在子类中定义和父类中相同名称的方法来实现类似于方法重载的特性。例如。 上述代码中,类B重载了类A的属性$attribute和方法func(),但类A的初始定义不会改变,例如,在以上代码的最后添加如下两行: $a=new A; $a- func(); /输出“父类A“,5.2.7 类的继承,
18、3. 使用final关键字 PHP 5引入了final关键字,在声明类时使用这个关键字,将使这个类不能被继承。例如,定义一个类A: final class A /. 如果类B尝试继承类A,将会提示以下错误: Fatal error: Class B may not inherit from final class (A) 另外,如果将final关键字用于声明类中的方法,该方法将不能在任何子类中重载。,5.2.8 抽象类和接口,1. 抽象类 抽象类也是PHP 5引入的新特性,它是一种特殊的类,使用关键字abstract来定义,不能被实例化。一个抽象类中至少包含一个抽象方法,抽象方法也是由abst
19、ract关键字来定义。抽象方法只提供了方法的声明,不提供方法的具体实现。例如: abstract function func($name, $number); 包含抽象方法的类必须是抽象类。 抽象类不能用于创建对象,所以只能通过继承来使用。继承抽象类的子类,必须重载抽象类中的所有抽象方法才能被实例化。例如。,5.2.8 抽象类和接口,2. 接口 PHP只能进行单继承,即一个类只能有一个父类。为了解决这个问题,PHP 5引入了接口的概念,接口是一个特殊的抽象类,使用interface关键字取代class关键字来定义。抽象类中允许存在非抽象的方法和属性,而在接口中定义的方法都是抽象方法。在接口中不
20、能使用属性,但可以使用const关键字定义的常量。例如: const con=“tom“; 接口的定义方法和定义类的方法类似,在接口中定义抽象方法不使用abstract关键字。例如: ,5.2.8 抽象类和接口,接口和类一样,也支持继承,接口之间的继承也使用extends关键字。例如:定义了接口之后可以将其实例化,接口的实例化称为接口的实现。要实现一个接口需要一个子类来实现接口的所有抽象方法。定义接口的子类使用implements关键字,另外,一个子类还可以实现多个接口,这样就解决了多继承的问题。例如。,5.2.8 抽象类和接口,一个子类还可以同时继承一个父类和多个接口,例如,假设类base和
21、上面代码中的接口已经创建,创建一个子类继承它们可以使用如下代码: class test extends base implements Teacher, Stu /类内容省略 ,5.2.9 类的魔术方法,1. 克隆对象 PHP使用clone关键字建立一个与原对象拥有相同属性和方法的对象,这种方法用于通过一个类实例化两个类似的对象的情况下。例如: $new_obj=clone $old_obj; $new_obj是新的对象名,$old_obj是要克隆的对象名。 克隆后的对象拥有克隆对象的全部属性,如果需要改变这些属性,可以使用PHP提供的魔术方法_clone。这个方法在克隆一个对象时将被自动调用
22、,类似于_construct和_destruct方法。在_clone方法中,可以定义确切的复制行为或执行一些操作。例如: id=$this-id+1; $c1=new Cid; $c2=clone $c; echo $c1-id; /输出1 echo $c2-id; /输出2 ?,5.2.9 类的魔术方法,2. 方法重载 PHP 5中有一个魔术方法_call,该方法可以用于实现方法的重载。_call方法必须要有两个参数。第一个参数包含被调用的方法名称,第二个参数包含传递给该方法的参数数组。_call方法在类中的方法被访问时被调用。例如。 3. 访问类的属性 通常情况下,从类的外部直接访问类的属
23、性是不建议使用的方法。这时可以使用_get和_set方法来检查和设置属性的值,这样就可以实现封装性。例如。,5.2.9 类的魔术方法,4. 字符串转换 由于类创建的对象的数据类型是对象,所以不能用print或echo语句直接输出。如果要输出对象,可以在类中定义一个_toString方法,在方法中返回一个可输出的字符串。例如: foo = $foo;public function _toString()return $this-foo; $class = new TestClass(Hello); echo $class; /输出Hello ?,5.2.9 类的魔术方法,5. 自动加载对象 _a
24、utoload方法用于自动加载对象,它不是一个类方法,而是一个单独的函数。如果脚本中定义了_autoload函数,当使用new关键字实例化一个没有声明的类时,这个类的名字将作为参数传递给_autoload函数,_autoload函数根据参数自动包含含有类的文件,并加载类文件中的同名类。例如: ,5.2.9 类的魔术方法,6. 对象序列化 对象序列化是指将一个对象转换成字节流的形式,将序列化后的对象在一个文件或网络上传输,然后再反序列化还原为原数据。对象序列化使用serialize()函数,反序列化使用unserialize()函数。在对象序列化时,如果存在魔术方法_sleep,PHP会调用_s
25、leep方法,主要用于清除如数据提交、关闭数据库链接等工作,并返回一个数组,该数组包含需要序列化的所有变量;在反序列化一个对象后,PHP会调用_wakeup方法,主要用于重建对象序列化时丢失的资源。这两个魔术方法都不接收参数。例如。,5.2.10 实例类型的判断,当系统很大时,往往需要判断某个对象是否是某个类创建的。这时可以使用instanceof关键字来实现。instanceof关键字是PHP 5新引入的,它可以检查一个对象的类型,判断一个对象是否是特定类的实例,是否继承于某个类或实现某个接口。用法如下: $var instanceof class_name; 如果变量$var是类class_name创建的对象,则返回TRUE,否则返回FALSE。例如: ,5.2.11 实例设计一个学生管理类,【例5.2】 设计一个学生管理类,用于获取学生信息。 新建EX5_2.php文件,输入以下代码。 执行结果如图5.2所示。,图5.2 学生管理类,