1 基础
先看一个简单的类的声明
a;
}
}
$obj = new SimpleClass();
$name = "SimpleClass";
$obj = new $name();
?>
对象的创建由new完成,new可以使用类名,或者是类名的字符串变量来生成对象。在类的上下文中,可以通过new self和new parent来创建对象。
当把一个变量指向的对象赋值给新的变量时,新旧变量访问的是同一个对象实例。若需要创建对象复本,需要使用clone来进行。当然我们可以把类实现成工厂,通过一个静态接口返回新的对象。PHP5.3支持下面的方法来返回一个新的对象实例。
2 类成员
类成员可以由public、private和protected后加上正常的变量声明即可。成员变量可以直接初始化,但只能初始化为常量。
在类的上下文,非静态的成员变量以$this->property的方式来访问。静态成员变量只能以self::$property的方式来访问。
toArray());
/* outputs:
Array
(
[var1] => 1
[ * var2] => 2
[ test var3] => 3
)
*/
?>
3 类常量
类中可以定义常量,常量不能以$开头,且必须为常量表达式,而非变量、成员属性或函数调用。
类常量的访问与类静态成员类似,只是不需要以$开头。
4 自动加载类
当在不同的文件中实现了多个类时,需要在使用这些类的时候包含这些文件,这需要在文件前面写很多include或require。为了解决这个问题PHP5.3定义了__autoload函数,当需要使用某个类时,可以通过此函数提供的实时加载功能来完成类文件的包含。当然这需要你以一定的方式来命令类对应的文件名。
5 构造与析构
对于构造函数,在派生的情况下,需要由子类显式的调用基类的构造函数。对于析构,也是相同的情况。
6 范围解析::
范围解析提供允许我们访问类的静态变量、类常量或类的重载方法。关键词self,parent以及static允许我们在类定义的内部访问类的方法和属性。
self和parent的用法见上面的代码即可。
7 static关键字
类的静态方法和静态成员可以不需要类实例而进行访问。静态成员不能通过$this伪变量访问,同时在静态方法的内部$this变量是无效的。
类的静态成员变量只能被初始化为字面常量。表达式是不允许的。
8 抽象类
PHP引入了抽象类与抽象方法的概念。与C++类似,抽象类不能被实例化,包含抽象方法的类必须是抽象类,抽象方法不能在有实现部分。
对于抽象类的派生类,必须实现所有抽象方法且定义为相同的可见性。
getValue() . "\n";
}
}
class ConcreteClass1 extends AbstractClass
{
protected function getValue() {
return "ConcreteClass1";
}
public function prefixValue($prefix) {
return "{$prefix}ConcreteClass1";
}
}
$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";
?>
接口定义了必须实现的方法集,接口中的方法为抽象且public的,因此派生类必须实现所有的抽象方法。接口中可以拥有常量。
接口之间可以多重继承。
interface a
{
public function foo();
}
interface b extends a
{
public function baz(Baz $baz);
}
// This will work
class c implements b
{
public function foo()
{
}
public function baz(Baz $baz)
{
}
}
10 Traits
Traits是PHP引入用于实现代码重用目的的,可以理解为共行。Traits与类类似,但只用于组织函数,其不能用于实例化。下面演示了traits的用法。
sayHello();
$o->sayWorld();
$o->sayExclamationMark();
?>
//output : Hello World!
若多个traits实现了同名函数,则使用这些traits的类会出现命名冲突,为了解决冲突需要使用insteadof..as...关键词。下面的代码使用A的bigTalk以及By的smalTalk方法。
trait HelloWorld {
use Hello, World;
}
?>
此外traits中的方法也可以抽象及静态的,并可以拥有成员属性,但从本意上traits是为了实现方法共用,抽象方法及属性的加入会导致与traits的配音有所出入。
11 拦截器
用PHP的术语叫做Overloading。即当访问类的不可访问的属性和方法时,会通过拦截器定义的魔术方法来动态的执行一定的行为。
11.1 属性拦截器
public void __set(string $name,mixed $value) 当向不可访问的属性写数据时调用
public mixed __get(string $name) 当读取不可访问的属性时调用
public bool __isset(string $name) 当用isset或empty一个不可访问的属性时调用
public void __unset(string $name) 当调用unset一个不可访问的属性时调用
属性拦截只能在对象上下文中工作,不能在静态上下文中工作。需要注意的属性拦截器会破坏原始类的封装性,因为当访问一个私有属性时也会通过拦截器返回某些东西。
data[$name] = $value;
}
public function __get($name)
{
echo "Getting '$name'\n";
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
$trace = debug_backtrace();
trigger_error(
'Undefined property via __get(): ' . $name .
' in ' . $trace[0]['file'] .
' on line ' . $trace[0]['line'],
E_USER_NOTICE);
return null;
}
/** As of PHP 5.1.0 */
public function __isset($name)
{
echo "Is '$name' set?\n";
return isset($this->data[$name]);
}
/** As of PHP 5.1.0 */
public function __unset($name)
{
echo "Unsetting '$name'\n";
unset($this->data[$name]);
}
/** Not a magic method, just here for example. */
public function getHidden()
{
return $this->hidden;
}
}
echo "\n";
$obj = new PropertyTest;
$obj->a = 1; //Seting 'a' to '1'
echo $obj->a . "\n\n"; //Geting 'a' 1
var_dump(isset($obj->a)); //Is 'a' set? bool(true)
unset($obj->a); //Unseting 'a'
var_dump(isset($obj->a)); //Is 'a' set? bool(false)
echo "\n";
echo $obj->declared . "\n\n"; // 1
echo "Let's experiment with the private property named 'hidden':\n";
echo "Privates are visible inside the class, so __get() not used...\n";
echo $obj->getHidden() . "\n"; //2
echo "Privates not visible outside of class, so __get() is used...\n";
echo $obj->hidden . "\n"; // Notice: Undefined property via __get(): hidden in on line 70 in on line 29
?>
11.2 方法拦截器
public mixed __call(string $name,array $args) 当访问不可见方法时调用
public static mixed __callStatic(string $name, array $args); 同上,用于静态上下文中
runTest('in object context');
MethodTest::runTest('in static context'); // As of PHP 5.3.0
?>
对象可以看做一个数组,通过foreach来访问其中的成员。如下例所示:
$value) {
print "$key => $value\n";
}
}
}
$class = new MyClass();
foreach($class as $key => $value) {
print "$key => $value\n";
}
echo "\n";
$class->iterateVisible();
?>
/* 输出如下
var1 => value 1
var2 => value 2
var3 => value 3
MyClass::iterateVisible:
var1 => value 1
var2 => value 2
var3 => value 3
protected => protected var
private => private var
对象也可以实现Iterator接口,来自行控制如何输出内容。
13 魔法方法
除了前面见到的以__开头的方法,下面这些方法 __sleep() __wakeup() __toString() __invoke() __set_state() __clone() 也是魔法方法。
public array __sleep() 当调用serialize()时,会首先调用定义的__sleep()方法,其返回类所有需要序列化的变量名,若方法没有返回数据,则NULL被序列化。
void __wakeup() 当调用unserialize()时,会检查__wakeup()并调用。
server = $server;
$this->username = $username;
$this->password = $password;
$this->db = $db;
$this->connect();
}
private function connect()
{
$this->link = mysql_connect($this->server, $this->username, $this->password);
mysql_select_db($this->db, $this->link);
}
public function __sleep()
{
return array('server', 'username', 'password', 'db');
}
public function __wakeup()
{
$this->connect();
}
}
?>
public string __toString() 当直接输出一个类时,会首先查找类定义的__toString()方法来字符串化一个类。
mixed __invoke([$...]) 当直接调用一个对象时,会查找类定义的此方法并调用之。
var1 = $an_array['var1'];
$obj->var2 = $an_array['var2'];
return $obj;
}
}
$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';
eval('$b = ' . var_export($a, true) . ';'); // $b = A::__set_state(array(
// 'var1' => 5,
// 'var2' => 'foo',
// ));
var_dump($b);
?>
14 final关键字
final可以用于类与方法的声明。对于final类,其不可被继承,对于final方法,派生类不可以覆盖其方法。
15 对象复制
PHP中对象默认按引用传递,因此当需要复制一个对象时,需要显式的调用clone来复制对象,此时类定义的__clone方法会被调用。类似于C++中的copy构造函数。
void __clone ( void )
instance = ++self::$instances;
}
public function __clone() {
$this->instance = ++self::$instances;
}
}
class MyCloneable
{
public $object1;
public $object2;
function __clone()
{
// Force a copy of this->object, otherwise
// it will point to same object.
$this->object1 = clone $this->object1;
}
}
16 比较对象
== 若两个对象是一个类的实例,且属性值相等
=== 还要求两个对象是同一个实例
17 类型指示
通过在函数原型中指定参数类型为对象、接口、数组或callable,PHP可以对参数的类型进行检查。若若或接口来指示类型,则子类或实现也是被允许的。
需要注意的是标准离散类型如int等不允许使用。
18 延迟静态绑定
用于在继承范围内引用静态调用的类。延迟绑定的意思为,static::不再被解析为定义当前方法的类,而是实际运行时的类。
采用原始的self::或者__CLASS__对当前类的引用,取决当前方法所在类范围。
19 对象与引用
通过下面的例子来看一下PHP中的对象默认以引用传递的含意。
$b->foo = 2;
echo $a->foo."\n"; //2
$c = new A;
$d = &$c; // $c and $d are references
// ($c,$d) =
$d->foo = 2;
echo $c->foo."\n"; //2
$e = new A;
function foo($obj) {
// ($obj) = ($e) =
$obj->foo = 2;
}
foo($e);
echo $e->foo."\n"; //2
?>
20 对象序列化
PHP对象都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialinze()函数能把字符串重新变回PHP对象。序列化一个对象只会保存对象的所有变量,不会保存对象的方法。
为了能unserialize()一个对象,对象的类定义必须有。要想在另一个文件unserialize()对象,可以通过包含定义此类的文件或使用spl_autoload_register()来实现。
one;
}
}
// page1.php:
include("classa.inc");
$a = new A;
$s = serialize($a);
// 把变量$s保存起来以便文件page2.php能够读到
file_put_contents('store', $s);
// page2.php:
// 要正确了解序列化,必须包含下面一个文件
include("classa.inc");
$s = file_get_contents('store');
$a = unserialize($s);
// 现在可以使用对象$a里面的函数 show_one()
$a->show_one();
?>