PHP class

一:结构和调用(实例化):

class className{} ,调用:$obj = new className();当类有构造函数时,还应传入参数。如$obj = new className($v,$v2…);

二:构造函数和析构函数:

1、构造函数用于初始化:使用__construct(),可带参数。

2、但析构函数不能带参数(用于在销去一个类之前执行一些操作或功能)。析构函数用__destruct()做名称。在脚本执行结束时,会销掉内存中的对象,因此可不用析造函数,但有些比如COOKIE等,就应当要用此函数销掉。

知识点:在PHP4中也提供了构造函数,但使用的是与类同名的类方法,在PHP5仍能兼容这种做法,当一个类中没有包含__construct时,会查找与类同名的方法,如果找到,就认为是构造函数,如下:

class test
{ var $b;
function test() { $this->b=5; }
function addab($c) { return $this->b+$c; }
}
$a = new test(); echo $a->addab(4); // 返回 9

3、PHP不会自动调用父类的构造函数(不支持构造函数重载),必须使用parent关键字显式地调用。

class employee{
function __construct()….
}
class Manager extents Employee{
function __construct(){
parent::_construct();
echo ‘这个子类的父类构造函数调用了!';
}
}
当然也可以调用与该实例没有任何关系的其它类的构造函数。只需在__construct()前加上类名即可。如:
otherClassName::__construct();

类的主家庭成员:属性、方法、常量、静态成员

三、类的属性:
有两种方法对类的属性赋值或取值。
1、使用公共作用域public关键词。
2、使用__set()和__get()来分别赋值和取值,前者称为设置方法(setter)或修改方法(mutator),后者称为访问方法(accessor)或获取方法(getter)。建议使用这种方法:优点:
A、可在__set()统一进行数据验证。
B、便于统一管理属性。

注意:

第一:__set()和__get()只对私有属性起作用,不设置方法,私有属性在实例后是不能访问和设置的。对于用public定义的属性,它们两个都懒理搭理,如下:

class test{
	protected $a=8,$b=2,$c;
	public $d=1;
	function __set($n,$v) { $this->$n = $v+6; }
	function __get($name) { return $this->$name+3; }
}
$a = new test();
$a->b =4; 
echo $a->a; //11  获取会走__get  8 + 3 返回   11
echo '<br />';
echo $a->b; //13  因为上面设置会走__set 4+6 获取会走__get  10 + 3 返回  13
echo '<br />';
$a->d=5; 
echo $a->d;//5
实例只对$a,$b,$c的设置会经过__set和__get过滤与返回,对于$d,就不会起作用。如$a->d=5,再返回还是5。

第二:__set($n,$v)要带两个参数。而__get($n)只能有一个参数。实例:

class test{
	private $a=5,$b=6,$c;
	function __set($n,$v) {	
		//$n => $e->a = 11;  a  /$n => $e->b = 12;  b 
		//$v => $e->a = 11;  11  /$v => $e->b = 12;  12  
		if($n=='b'&&$v>0)
			$this->$n = $v;
		else
			$this->$n = $v+2;
	}

	function __get($name) {	
		return $this->$name + $this->addab();
	}

	function addab() { 
		return $this->a + $this->b; 
	}
}
$e=new test();
$e->a = 11; //注意写法:类的内部用$this->$n即变量的写法,但外部实例要用$e->a的方式。 $e->a = 13
$e->b = 12; //12        因为符合条件没有+2
$e->d = 22;
//$e->k = 22;
echo '<br>';
echo $e->b; //进入__get $this->$name = $e->b = 12 +    13 + 12  = 37
echo $e->d;  //24  进入__set

类的属性可自由扩展,如上例的d,不管是否用__set,当一个实例建立起来后,可以用$e->newProperty = xx;直接来创造一个属性,但不建议这么做。
四、类的方法:理解成类当中的函数即可。
调用:
1、内部调用:可使用$this->Fanname();或$this->addab()或test::addab();
2、实例化调用时,用$e->addab();即可。对于在该方法中没有使用$this关键字的,如上例中的:function addab() { return $this->a+$this->b; }改为: function addab() { return 25; }那在在外部实例调用该方法,也可用“$e::addab();”或“test::addab();”
五、类的常量:如果类的属性理解成类中的变量,那么类的常量和变量是不一样的,其定义方法为

class test{
	private $a=1;
	const PI = '3.14';
	//在类中调用上面的常量用两种方法,“$this::PI”,或 “类名::PI”,这里就是test::PI,如下:
	function getvalue(){
		return $this->a * $this::PI; //或$this->a * test::PI,用this关键字或类名均可,但都要用双冒号。
	}
}
$e= new test();
$e->PI =5; //注意,这里用 ->只是创造了一个也是名为PI的属性,而不是改变类中的PI常量的值。
echo $e::PI; //这个才是调用类的常量。
echo $e->PI; //5
常量只能用双冒号::来调用。并且不能更改其值。
在类外部实例化后调用类常量同样也有两种方法。方法为:
“$e::PI” 或 “test::PI”,共同点是都要用冒号,不同点是外部不能用this关键字,只能用实例名,但类名::PI是通用的。
六、类的静态成员(静态属性或静态方法):
如果需要创建供所有类的实例共享的字段或方法。就得用静态成员。有两个特征:
1、静态成员是共产主义者,它让脚本上的所有该类的实例调用,但不能借助类的特定实例名调用,而是在类的外部,统一使用“类名::$成员名”的方式调用。而类的内部则统一使用 “self::$成员名”来调用。静态方法直接调用:: 不用new 
2、当每一次新创建实例时,静态成员会从上次创建的实例最后值开始重新计算,而不是类中初始的值开始计算。
3、对于用public定义的静态成员,可以在外部更改它的值。private等则不行。
class test{
	public static $v = 0;
	function __construct(){ self::$v++; }
	static function getV(){ return self::$v; }
}
$a = new test();
echo test::getV(); // 返回 1
$b = new test();
echo test::getV(); // 返回 2
test::$v=8; //由于public定义的成员,改变静态成员的值。 如果不是public不能改变值
$c = new test();
echo test::getV(); // 返回 9
七、关键字:
(一)this关键字:用于类的内部指代类的本身。来访问属性或方法或常量,如$this->属性名或方法名。$this::常量名。this还可以用在该类的子类中,来指代本身的属性或方法。
(二)双冒号“::”关键字:用于调用常量、静态成员。
(三)self关键字:在类的内部与双冒号配合调用静态成员,如 self::$staticVar.,在类的内部,不能用$this来调用静态成员。
(四)__toString():在类中使用__toString(),用于将类转成字串并打印类,用处不大:如:
class test{ public $p;
public function __toString(){ return var_export($this,TRUE); }
}
$a=new test();
echo $a; //输出:test::__set_state(array( ‘p' => NULL, )),或写成:echo $a->__toString();
(五)__clone() :当克隆对象时,这个关键字才会发生作用,用于更改克隆时某些值。
class Account {
    public $balance;
     
    public function __construct($balance) {
      $this->balance = $balance;
    }
  }
  
  class Person {
    private $id;
    private $name;
    private $age;
    public $account;
     
    public function __construct($name, $age, Account $account) {
      $this->name = $name;
      $this->age = $age;
      $this->account = $account;
    }
     
    public function setId($id) {
      $this->id = $id;
    }
     
    public function __clone() {  #复制方法,可在里面定义再clone是进行的操作
      $this->id = 0;
      $this->account = clone $this->account;  #不加这一句,account在clone是会只被复制引用,其中一个account的balance被修改另一个也同样会被修改
    }
  }
   
  $person = new Person("peter", 15, new Account(1000));
  $person->setId(1);
  $person2 = clone $person;
   
  $person2->account->balance = 250;
   
  var_dump($person, $person2);
object(Person)#1 (4) { ["id":"Person":private]=> int(1) ["name":"Person":private]=> string(5) "peter" 
["age":"Person":private]=> int(15) ["account"]=> object(Account)#2 (1) { ["balance"]=> int(1000) } } 
object(Person)#3 (4) { ["id":"Person":private]=> int(0) ["name":"Person":private]=> string(5) "peter" 
["age":"Person":private]=> int(15) ["account"]=> object(Account)#4 (1) { ["balance"]=> int(250) } }

clone方法克隆出了一个新的类,所以此时内存中有两个对象。
php的__clone()方法对一个对象实例进行的浅复制,对象内的基本数值类型进行的是传值复制,而对象内的对象型成员变量,如果不重写__clone方法,显式的clone这个对象成员变量的话,这个成员变量就是传引用复制,而不是生成一个新的对象.如下面一个例子的第28行注释所说
因为我们传值是一个对象,如果不可隆就相当于引用,修改一个值就都修改了,我们不调用clone直接用=赋值也是相当于引用。
(六)__call():方法重载,用于监视错误的方法调用。参下面示例:
该方法有两个参数,第一个参数 $function_name 会自动接收不存在的方法名,第二个 $args 则以数组的方式接收不存在方法的多个参数。
在类里面加入:
function __call($function_name, $args)
{
    echo "你所调用的函数:$function_name(参数:<br />";
    var_dump($args);
    echo ")不存在!";
}
当调用一个不存在的方法时(如 test() 方法):
$p1=new Person();
$p1->test(2,"test");
输出的结果如下:
你所调用的函数:test(参数:
array(2) {
    [0]=>int(2)
    [1]=>string(4) "test"
}
)不存在!
(七)extends:继承: 如class a{} class b extends a{} 类b继承了类a
附:记忆:以后统一在调用方法或属性时用 “-> “,调用常量则用双冒号“::”,不会搞晕。
八、方法和属性的作用域:
共有6种:public(默认,可省略,也等同于php6的var声明),private(私有,也不能由子类使用),protected(私有,但可由子类使用) ,abstract(抽象,参下文),final(阻止在子类中覆盖—也称重载,阻止被继承,用于修饰类名及方法,如final class test{ final function fun(){}} ,但不能用于属性);用于类,该类无法被继承,用于方法,类被继承后,子类不能重写父类中的final关键字方法;static(静态)
九:抽象类和抽象方法(abstract——注意:没有所谓抽象属性):
抽象可以理解成父类为子类定义了一个模板或基类。作用域abstract只在父类中声明,但在子类中实现。注意事项:
1、抽象类不能被实例化,只能被子类(具体类)继承后实现。
2、抽象类必须在其子类中实现该抽象类的所有抽象方法。否则会出错。
3、在抽象方法中,只是声明,但不能具体实现:如abstract function gettow(){ return $this->p; }是错的,只能声明这个方法:abstract function gettow();(连方括号{}都不要出现),抽象方法和抽象类主要用于复杂的类层次关系中。该层次关系需要确保每一个子类都包含并重载了某些特定的方法。这也可以通过接口实现
4、属性不能被命名为抽象属性,如abstract $p = 5是错的。
5、只有声明为抽象的类可以声明抽象方法,但如果方法声明为抽象,就不能具体实现。如:
abstract class Employee
{
abstract function a(…);
abstract function b(…);
}
以后再对这个父类扩展,组成各种子类(如经理,员工,出纳)。
6、抽象类中,如果要实现具体的方法,不能声明为抽象。这样可能实际意义更大。可以把几个类库中共同的部分提取到抽象类中,其它的类继承抽象类即可。
十:类型提示:
注意,类型提示功能只能用于参数为对象的提示,而无法用于为整数,字串,浮点等类型提示。有些类的方法需要传入的参数为所期望的对象类型,可以用下面的方法达到强制实施此替则。要达到类型提示,只要在方法的对象型参数前加一个已存在的类的名称,如:function funname(OtherClassName $otherclassINSName,$c….),注意,OtherClassName必须是存在的类。如下:
class em{ var $k=56; }
class test{
function __construct()
{ echo $this->addab(new em(),2); }
function addab(em $j,$c) //这个方法,即可以在内部调用,也可以在外部调用。只要作用域许可。
{ return $j->k+$c; }
}
$a = new test();
$b = new em();
echo $a->addab($b,2); //或 $a->addab(new em(),2);
十一、自动加载类库文件:
当类多了以后,比如要在一个文件中载入3个类库文件:a.class.php,b.class.php,c.class.php要用三个require_once('classes/a.class.php');
require_once('classes/b.class.php');
require_once('classes/c.class.php');
可以用PHP5自动加载的功能来处理:在全局应用配置文件中,定义一个特殊的函数__autoload($class)函数(__autoload并不是一个类的方法,只是单独的函数,和类没有关系):
function __autoload($class){
require_once(“classes/$class)
}
该函数放哪没有关系,在创建类实例时,也不必去调用这个autoload函数。PHP会自动完成。但务必注意一点:“在调用页面上创建实例所使用的类名称”、和“被调用的文件名”、以及“该文件中的类的名称”3个必须是一样的。这样就不需要去调用__autoload();如果不一样则必须单独调用__autoload(‘c');并给它一个文件名前缀。如:
c.class.php文件的代码是:
class c{
public $m=7;
}
这里代码的类名称是c,而文件名也是c,
现在要在index.php调用:
function __autoload($class){
require_once “$class.class.php”;
}
$m = new c(); //创建实例调用的类也是c
echo $m->m;
此时PHP会自动调用根目录下的c.class.php中的类C。
但如果c.class.php中的代码是:
class mm{
public $m=7;
}
而调用页index.php代码是:
function __autoload($class){
require_once “$class.class.php”;
}
# __autoload(‘c'); //如果不加这一行就会出错。
$m = new mm();
echo $m->m;
会出错,提示找不到mm.class.php文件。这时可以加一行__autoload(‘c');但这样就达不到简化代码的目的。
类的家族化扩展:类的高级功能:


一、对象克隆:
当克隆一个对象的实例时,其属性初始值继承了被克隆对象的当前值。
class test
{
public $p=5;
function __clone(){ //只在克隆发生时起作用。用于改变在克隆时某些值
$this->p=15;
}
}
$a=new test();
echo $a->p;
$a->p=8; //如果没有__clone()方法影响,$b的P值将为8
$b = clone $a;
echo $b->p; //15

二、对象继承:
没有被声明为final的类可以被继承,没有被final和private界定的方法也可以继承,没有被private界定的属性也可以继承。当子类继承了父类或超类后,可以直接使用父类或超类(祖父类以及祖父的祖父)的所有允许的方法,属性。
关键:理解构造函数和重载在继承中的特性!
(一)构造函数在继承中的特性:
1、当父类有构造函数而子类没有:则子类会在实例化时会自动执行父类的构造函数。这时如果要创建子类的实例,需要引入父类构造函数中所需的参数,否则出错。即使是“子类的子类”如果没有构造函数,也要在创建实例时输入其父类的父类的构造函数所需参数。PHP会从实例所在的子类会向上搜索合造的构造函数,一旦找到就停止,使用该构造函数。而不会再向上搜索,因此:子类本身如果没有构造函数,则以其最靠近的一个超类并且有构造函数的为准。

class cA{
public $name,$age;
function __construct($n) {
$this->name = $n;
$this->age = 25;
}
function __set($n,$v) {
$this->$n = $v;
}
function __get($n) {
return $this->$n;
}
}
class cB extends cA{
function funB1() { echo ‘<h3>Class cB execute success!</h3>'; }
}
class cC extends cB {
function funC1() { echo ‘<h3>Class cC FunC1!</h3>'; }
}
$b=new cB(‘Jack');
$b->name='John';
echo “$b->name : $b->age”;
$b->funB1();
$c=new cC(); //这里会出错,由于cB也没有构造函数,因此再向上以cA为准,需要一个参数。改为$c=new cC(‘David');即可。
echo $c->name(); //David

2、当子类也有构造函数时:这时,不管父类是否有构造函数,都会执行子类自己的构造函数。

class cB extends cA{
function __construct() {
echo ‘<h3>this is Class cB \'s __construct!</h3>';
}
function funB1() {
echo ‘<h3>Class cB execute success!</h3>';
}
}
现在类CB有自己的构造函数时,这时创建实例$b=new cB(‘Jack');参数JACK不会起作用,因为父类CA的构造函数没有得到执行。因此$b->name和$->age就不会初始化值。需要另外赋值$b->name='Jack',$b->age=25;
如果这时要执行父类CA的构造函数,可以这样:
function __construct($n) {
parent::__construct($n); // 或:cA::__construct($n);
echo ‘<h3>this is Class cB \'s __construct!</h3>';
}
由于parent::__construct($n); 只会向上搜索父类的构造函数,一找到就停止且执行当前找到的构造函数,因此在上面例子中,如果parent::__construct($n)是用在最后一层的类cC中,并且类CB,CA都有构造函数,那么cC的实例只会执行cB的构造函数。不会执行cA。这时,如果CC的实例想都调用CA和CB的构造函数,有两种方法:
A、在CB中也加入parent::__construct($n)
B、在CC中把构造函数改为:
function __construct($n) {
cA::__construct($n); //即:类名::构造函数。
cB::__construct();
echo ‘<h3>this is Class cB \'s __construct!</h3>';
}

(二)在子类中调用父类的属性或方法:
1、调用父类方法:在子类中调用父类的方法,有3种方法:
$this->ParentFunction(); 或
父类名::ParentFunction(); 或
parent::parentFun();
2、调用父类属性:只能用$this->ParentProperty;
(三)重载:
在子类中,可以定义与父类相同属性或方法,改变父类该属性或方法的值或操作,称做重载。如:
calss ParClass{ function pfun(){ ….}}
class ChildrenClass extends ParClass{function pfun(){ ….}}} //重载了父类的pfun的方法。
在子类中重载后,优先执行自己重载后的新定义的方法或属性。
也可以在子类中用parent::parentFun();调用父类的方法,但所得到的值是子类自己输入的参数运算值。而不是该方法在父类中运算的值。


三、接口:
接口:interface,可以理解成一组功能的共同规范,最大意义可能就是在多人协作时,为各自的开发规定一个共同的方法名称。
和抽象类中的抽象方法一样:
1、不能在接口中对方法具体实现进行定义。而是由具体类来实现(而抽象类中的非抽象方法可以不必再定义,只有抽象方法和接口是一样要求要在具体类中实现)。
2、和抽象类一样,可以在接口中定义常量,并由具体类直接继承。
3、具体类必须实现抽象类的所有抽象方法(非抽象方法除外),同样,具体类如通过implements实现了接口后,必须完成接口中的所有方法。
接口实现过程:1、定义接口,2、用..implement X,Y,…和具体类对接。

interface Info{ //定义接口
const N=22;
public function getage();
public function getname();
}
class age implements Info //如要多个接口 class age (extends emJob) implements Info,interB…
{
public $age=15;
public $name='Join';
function getage() {
echo “年级是$this->age”;
}
function getname() {
echo “姓名是$this->name”;
}
function getN(){
echo ‘<h3>在接口中定义的常量N的值是:'.$this::N.' </h3>'; //直接继承接口中的常量值。
}
}
$age=new age;
echo $age::N; //22,直接调用接口中的常量值。
$age->getN();

关于抽象类和接口类的使用区分:何时用接口,何时用抽象?
1、相关性:当创建的模型由一些紧密相关的对象采用时,用抽象。对于不相关对象采用的功能,用接口。
2、多重继承:PHP类可以继承多个接口,但不能扩展多个抽象类。
3、公共行为实现:抽象类可在其中实现公共的方法,但接口不行。


使用Reflection(反射)API 。
简易实例:
class a{ …. }
$c = new ReflectionClass(‘a'); //PHP 内置类。
echo ‘<pre>'.$c.'</pre>';
输出类a的结构和内容。参《PHP圣经》P145;


原文:http://www.jb51.net/article/42172.htm

 
 
 
 
 
 
 
 
 

猜你喜欢

转载自blog.csdn.net/cail_520914/article/details/77482064