·您现在的位置: 云翼网络 >> 文章中心 >> 网站建设 >> 网站建设开发 >> php网站开发 >> 类与对象

类与对象

作者:佚名      php网站开发编辑:admin      更新时间:2022-07-23
类与对象 - php手册笔记基本概念

PHP对待对象的方式与引用和句柄相同,即每个变量都持有对象的引用,而不是整个对象的拷贝。

当创建新对象时,该对象总是被赋值,除非该对象定义了构造函数并且在出错时抛出了一个异常。类应在被实例化之前定义。

创建对象时,如果该类属于一个名字空间,则必须使用其完整名称。

在类定义内部,可以用new selfnew parent创建对象。

<?php $instance = new stdClass();$assigned = $instance;$reference = & $instance;$instance->var = '$assigned will have this value.';$instance = null;var_dump($instance);var_dump($reference);var_dump($assigned);

这段代码的输出如下,这是为什么呢?

nullnullobject(stdClass)[1]  public 'var' => string '$assigned will have this value.' (length=31)

PHP 5.3引进了两个新方法来创建一个对象的实例,可以使用下面的方法创建实例。

<?php class Test {static public function getNew() {return new static;}}class Child extends Test {}$obj1 = new Test();$obj2 = new $obj1;var_dump($obj1 !== $obj2);  // true$obj3 = Test::getNew();var_dump($obj3 instanceof Test);  // true$obj4 = Child::getNew();var_dump($obj4 instanceof Child);  // truevar_dump($obj1 == $obj2);  // true

PHP不支持多重继承,被继承的方法和属性可以通过同样的名字重新声明被覆盖,注意参数必须保持一致,当然构造函数除外。但是如果父类定义方法时使用了final,则该方法不可被覆盖。可以通过parent::来访问被覆盖的方法和属性,parent::只能访问父类中的常量const,不能访问变量。

<?php class A {PRivate $name = 'A';const conname = 'A';public function getName() {return $this->name;}}class B extends A {private $name = 'B';const conname = 'B';public function getName() {return $this->name;}public function getParent() {return parent::conname;}}class C extends B {private $name = 'C';const conname = 'C';public function getName() {return $this->name;}public function getParent() {return parent::conname;}}$a = new A;var_dump($a->getName());  // A$b = new B;var_dump($b->getName());  // Bvar_dump($b->getParent());  // A$c = new C;var_dump($c->getName());  // Cvar_dump($c->getParent());  // B

自PHP 5.5起,关键词class也可用于类名的解析。使用ClassName::class你可以获取一个字符串,包含了类ClassName的完全限定名称。

<?php namespace NS {class ClassName {}echo ClassName::class;  // NS\ClassName}
属性

属性,也就是类的变量成员。属性中的变量可以初始化,但是初始化的值必须是常数。这里的常数是指 PHP 脚本在编译阶段时就可以得到其值,而不依赖于运行时的信息才能求值。

在类的成员方法里,访问非静态属性使用$this->property,访问静态属性使用self::$property。静态属性声明时使用static关键字。

类常量

在定义常量时不需要$符号和访问控制关键字。

接口(interface)中也可以定义常量。

自动加载类

写面向对象的应用程序时,通常对每个类的定义简历一个PHP源文件。当某个文件需要调用这些类时,需要在文件开头写一个长长的包含文件列表。其实,并不需要这样,可以定义一个__autoload()函数,它会在试图使用尚未被定义的类时自动调用。

手册Tip说,spl_autoload_register()提供了一种更加灵活的方式来实现类的自动加载,这个后面再看。

自动加载不可用于PHP的CLI交互模式,也就是命令行模式。

用户输入中可能存在危险字符,起码要在__autoload()时验证下输入。

可以通过下面的方式自动加载类。

<?php function __autoload($class_name) {require_once $class_name.'.php';}$obj1 = new MyClass1();$obj2 = new MyClass2();

对于异常处理,后面再看。

构造函数和析构函数

PHP 5允许开发者在一个类中定义一个方法作为构造函数,构造函数也不支持重载。

如果子类中定义了构造函数,则不会隐式调用父类的构造函数,否则会如同一个普通类方法那样从父类继承(前提是未被定义为private)。要执行父类的构造函数,需要在子类构造函数中调用parent::__construct()

与其它方法不同,当__construct()与父类__construct()具有不同参数时,可以覆盖。

自PHP 5.3.3起,在命名空间中,与类名同名的方法不再作为构造函数。

析构函数会在某个对象的所有引用都被删除或者对象被显示销毁时执行。析构函数即使在使用exit()终止脚本运行时也会被调用。

试图在析构函数中抛出异常,将会导致致命错误。

访问控制

类属性必须定义为公有、受保护、私有之一,不能省略关键字。如果类中方法没有设置访问控制的关键字,则该方法默认为公有。

同一个类的对象,即使不是同一个实例,也可以互相访问对方的私有与保护成员。示例程序如下。

<?php Class Test {private $foo;public function __construct($foo) {$this->foo = $foo;}private function bar() {echo 'accessed the private method.';}public function baz(Test $other) {$other->foo = 'hello';var_dump($other->foo);$other->bar();}}$test = new Test('test');$test->baz(new Test('other'));
对象继承

如果一个类扩展了另一个,则父类必须在子类前被声明。

范围解析操作符

范围解析操作符,简单地说就是一对冒号,可以用于访问静态成员、类常量,还可以用于调用父类中的属性和方法。

当在类定义之外引用这些项目时,要使用类名。

static

使用static关键字可以用来定义静态方法和属性,也可用于定义静态变量以及后期静态绑定。声明类属性或方法为静态,就可以不实例化类而直接访问。

静态属性不能通过一个类已实例化的对象来访问,但静态方法可以。

如果没有指定访问控制,属性和方法默认为公有。

用静态方法调用一个非静态方法会导致一个E_STRICT级别的错误。

抽象类

PHP 5支持抽象类和抽象方法。类中如果有一个抽象方法,那这个类必须被声明为抽象的。

抽象类不能被实例化。抽象方法只是声明了其调用方式(参数),不能定义其具体的功能实现。继承抽象类时,子类必须定义父类中的所有抽象方法,且这些方法的访问控制必须和父类一样活更宽松。

方法的调用方式必须匹配。但是,子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。这也试用与PHP 5.4起的构造函数。可以在子类中定义父类签名中不存在的可选参数。

<?php abstract class AbstractClass {abstract protected function prefixName($name);}class ConcreteClass extends AbstractClass {public function prefixName($name, $separator = ', ') {if($name === "Pacman") {$prefix = 'Mr';} elseif($name === 'Pacwoman') {$prefix = "Mrs";} else {$prefix = '';}return "$prefix $separator $name  ";}}$class = new ConcreteClass;echo $class->prefixName('Pacman');echo $class->prefixName('Pacwoman');
对象接口

听说过接口,一直没用过。使用接口,可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容,也就是说接口中定义的所有方法都是空的。接口中定义的所有方法都必须是公有的,这是接口的特性。

接口也可以继承多个接口,用逗号分隔,使用extends操作符。类中必须实现接口中定义的所有方法,否则会报错。要实现一个接口,使用implements操作符。类可以实现多个接口,用逗号分隔。实现多个接口时,接口中的方法不能有重名。类要实现接口,必须使用和接口中所定义的方法完全一致的方式。

接口中也可定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口覆盖。

traits

从PHP 5.4.0开始,可以使用traits实现代码复用。Traits 是一种为类似 PHP 的单继承语言而准备的代码复用机制。Trait 不能通过它自身来实例化。它为传统继承增加了水平特性的组合。

优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。

通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中。如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误,为解决冲突,需使用insteadof操作符来指明使用冲突方法中的哪一个,这种方法仅允许排除掉其它方法。as操作符可以将其中一个冲突的方法以另一个名称(别名)来引入。

<?php trait A {public function smallTalk() {echo 'a';}public function bigTalk() {echo 'A';}}trait B {public function smallTalk() {echo 'b';}public function bigTalk() {echo 'B';}}class Talker {use A, B {B::smallTalk insteadof A;A::bigTalk insteadof B;B::bigTalk as talk;}}$t = new Talker();$t->smallTalk();  // b$t->bigTalk();  // A$t->talk();  // B

使用as操作符还可以用来调整方法的访问控制,或者给方法一个改变了访问控制的别名,原版方法的访问控制规则没有改变。

<?php trait HelloWorld {public function sayHello() {echo 'Hello World.';}}class MyClass1 {use HelloWorld {sayHello as protected;}}class MyClass2 {use HelloWorld {sayHello as private myPrivateHello;}}

就像类能够使用trait那样,多个trait能够组合为一个trait

为了对使用的类施加强制要求,trait 支持抽象方法的使用。

<?php trait Hello {public function sayHelloWorld() {echo 'Hello ' . $this->getWorld();}abstract public function getWorld();}class MyHelloWorld {private $world;use Hello;public function getWorld() {return $this->world;}public function setWorld($val) {$this->world = $val;}}$c = new MyHelloWorld;$c->setWorld('world');$c->sayHelloWorld();

如果trait定义了一个属性,那类将不能定义同样名称的属性,否则会产生错误。

重载

PHP提供的重载是指动态地创建类属性和方法,与其它绝大多数面向对象语言不同。通过魔术方法来实现。当使用不可访问的属性或方法时,重载方法会被调用。所有的重载方法都必须被声明为public

使用__get()__set()__isset()__unset()进行属性重载,示例如下。

<?php class PropertyTest {private $data = array();public $declared = 1;private $hidden = 2;public function __set($name, $value) {echo "Setting $name to $value. " . '<br>';$this->data[$name] = $value;}public function __get($name) {echo "Getting $name. <br>";if(array_key_exists($name, $this->data)) {return $this->data[$name];}return null;}public function __isset($name) {echo "Is $name set? <br>";return isset($this->data[$name]);}public function __unset($name) {echo "Unsetting $name. <br>";unset($this->data[$name]);}}$obj = new PropertyTest;$obj->a = 1;var_dump($obj->a);var_dump(isset($obj->a));unset($obj->a);var_dump(isset($