PHP当中对象是按引用传递的,即每个包含对象的变量都持有对象的引用(reference),而不是整个对象的拷贝。
一、类
* 类定义
class 后接类名。伪变量$this在对象内部时调用,指向所属的对象的引用。
* 对象
new创建一个对象的实例。
* 继承
子类 extends 父类,只能继承一个基类。被继承的方法和成员可以通过相同的名字重新声明覆盖,除非父类定义方法时使用了 final 关键字。可以通过 parent:: 来访问被覆盖的方法或成员。
class ExtendClass extends SimpleClass { // Redefine the parent method function displayVar() { echo "Extending class\n"; parent::displayVar(); } } $extended = new ExtendClass(); $extended->displayVar();
* 属性
属性声明用public/protected/private修饰,后跟一个变量名。可以用var代替访问控制修饰符表示为public。也可以把var加在修饰符前面。
属性的访问在对象内部可以用$this->property来访问,静态属性用self::$property访问
* 类常量
类常量定义时不需要使用$符号。常量必须为一个定值,不能为变量,类属性,操作结果。
* 自动加载对象
使用__autoload 函数,在使用尚未被定义的类时,脚本引擎在出错失败前自动加载所需的类。
* 构造函数
类在每次创建新对象时先调用构造方法,适合在使用对象之前做初始化工作。
class BaseClass { function __construct() { print "In BaseClass constructor\n"; } } class SubClass extends BaseClass { function __construct() { parent::__construct(); //调用父类构造函数 print "In SubClass constructor\n"; } } $obj = new BaseClass(); $obj = new SubClass();
* 析构函数
析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
void __destruct ( void )
class MyDestructableClass { function __construct() { print "In constructor\n"; $this->name = "MyDestructableClass"; } function __destruct() { print "Destroying " . $this->name . "\n"; } } $obj = new MyDestructableClass();
* 访问控制
public 所有的都可以访问,未定义修饰符时,默认为public
protected 类本身及子类可以访问
private 类本身可以访问
* 范围解析操作符(::)
用于访问静态成员、方法和常量,覆盖类的成员和方法.
class OtherClass extends MyClass { public static $my_static = 'static var'; public static function doubleColon() { echo parent::CONST_VALUE . "\n"; echo self::$my_static . "\n"; } } OtherClass::doubleColon();
* 抽象类
抽象类不能被实例化,需继承该抽象类,实例化子类。任何一个类, 只要有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。如果类方法被声明为抽象的, 那么就不能有具体功能实现。
abstract class AbstractClass { // 强制要求子类定义这些方法 abstract protected function getValue(); abstract protected function prefixValue($prefix); // 普通方法(非抽象方法) public function printOut() { print $this->getValue() . "\n"; } } class ConcreteClass1 extends AbstractClass { protected function getValue() { return "ConcreteClass1"; } public function prefixValue($prefix) { return "{$prefix}ConcreteClass1"; } }
* 接口
接口(interface),可以指定类必须实现哪些方法,但不需要定义这些方法的具体内容。要实现一个接口,使用implements操作符,类中必须实现接口中定义的所有方法,实现多个接口,可以用逗号来分隔多个接口的名称。接口与接口之间也可以用extends操作符继承。
interface a { public function foo(); } interface b { public function bar(); } interface c extends a, b { public function baz(); } class d implements c { public function foo() { } public function bar() { } public function baz() { } }
* Traits
trait Hello { public function sayHello() { echo 'Hello '; } } trait World { public function sayWorld() { echo 'World'; } } class MyHelloWorld { use Hello, World; public function sayExclamationMark() { echo '!'; } } $o = new MyHelloWorld(); $o->sayHello(); $o->sayWorld(); $o->sayExclamationMark();
* 重载
属性重载
public void __set ( string $name , mixed $value )
public mixed __get ( string $name )
public bool __isset ( string $name )
public void __unset ( string $name )
在给未定义的变量赋值时,__set() 会被调用
读取未定义的变量的值时,__get() 会被调用。
当对未定义的变量调用isset() 或 empty()时,__isset() 会被调用。
当对未定义的变量调用unset()时,__unset() 会被调用。
参数$name是指要操作的变量名称。__set() 方法的$value 参数指定了$name变量的值。
方法重载
public mixed __call ( string $name , array $arguments )
public static mixed __callStatic ( string $name , array $arguments )
当调用一个不可访问方法(如未定义,或者不可见)时,__call() 会被调用。
当在静态方法中调用一个不可访问方法(如未定义,或者不可见)时,__callStatic() 会被调用。
* 对象迭代
实现Iterator接口来实现对象的foreach迭代。
class MyIterator implements Iterator { private $var = array(); public function __construct($array) { if (is_array($array)) { $this->var = $array; } } public function rewind() { echo "rewinding\n"; reset($this->var); } public function current() { $var = current($this->var); echo "current: $var\n"; return $var; } public function key() { $var = key($this->var); echo "key: $var\n"; return $var; } public function next() { $var = next($this->var); echo "next: $var\n"; return $var; } public function valid() { $var = $this->current() !== false; echo "valid: {$var}\n"; return $var; } } $values = array(1,2,3); $it = new MyIterator($values); foreach ($it as $a => $b) { print "$a: $b\n"; }
实现IteratorAggregate接口
class MyCollection implements IteratorAggregate { private $items = array(); private $count = 0; // Required definition of interface IteratorAggregate public function getIterator() { return new MyIterator($this->items); } public function add($value) { $this->items[$this->count++] = $value; } } $coll = new MyCollection(); $coll->add('value 1'); $coll->add('value 2'); $coll->add('value 3'); foreach ($coll as $key => $val) { echo "key/value: [$key -> $val]\n\n"; }
* 设计模式
工厂模式
单例模式
* 魔术方法
PHP把所有以__(两个下划线)开头的类方法当成魔术方法。所以当你定义类方法时,除了上述魔术方法,建议不要以 __为前缀。
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state() 和 __clone() 等方法在PHP中被称为"魔术方法"(Magic methods)。 你在命名自己的类方法时不能使用这些方法名, 除非你希望使用"魔术"功能
* final关键字
新增了一个 final 关键字。如果父类中的方法被声明为final,则子类无法覆盖该方法; 如果一个类被声明为final,则不能被继承。类似于C#中的seal。
* 对象复制
对象复制可以通过clone关键字。象被复制后,PHP5会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性仍然会是一个指向原来的变量的引用。
$copy_of_object = clone $object;
(如果可能,这将调用对象的__clone()方法)。对象中的 __clone()方法不能被直接调用。
* 对象比较
当使用对比操作符(==)比较两个对象变量时,如果两个对象的属性和属性值 都相等,而且两个对象是同一个类的实例,那么这两个对象变量相等。
如果使用全等操作符(===),这两个对象变量一定要指向某个类的同一个实例(即同一个对象)。
* 类型约束
函数的参数可以指定只能为对象
//如下面的类 class MyClass { /** * 测试函数 * 第一个参数必须为类OtherClass的一个对象 */ public function test(OtherClass $otherclass) { echo $otherclass->var; } /** * 另一个测试函数 * 第一个参数必须为数组 */ public function test_array(array $input_array) { print_r($input_array); } } //另外一个类 class OtherClass { public $var = 'Hello World'; }
* 后期静态绑定
static::不再被解析为定义当前方法所在的类,而是在实际运行时计算的。使用self:: 或者 __CLASS__对当前类的静态引用,取决于定义当前方法所在的类。后期静态绑定试图通过引入一个关键字表示运行时最初调用的类来绕过限制。简单地说,这个关键字能够让你在上述例子中调用test()时引用的类是B而不是A。最终决定不引入新的关键字,而是使用已经预留的static关键字。 后期静态绑定的处理方式解决了以往完全没有办法解决的静态调用。另外一方面,如果静态调用使用 parent:: 或者 self:: 将转发调用信息。
class A { public static function who() { echo __CLASS__; } public static function test() { self::who(); } } class B extends A { public static function who() { echo __CLASS__; } } B::test();//输出A
* 对象和引用
* 对象序列化
序列化函数serialize()将字节流转换成字符串,反序列化函数unserialize()将字符串转换成字节流
序列化一个对象会保存所有的变量,类的名称,但是不会保存类的方法。序列化之前必须先定义该类。可以通过包含一个定义该类的文件或使用函数spl_autoload_register()来实现。