对属性或方法的访问控制,是通过在前面添加关键字 public、protected 或 private 来实现的。由 public 所定义的类成员可以在任何地方被访问;由 protected 所定义的类成员则可以被其所在类的子类和父类访问(当然,该成员所在的类也可以访问);而由 private 定义的类成员则只能被其所在类访问。
类成员都必须使用关键字public、protected 或 private 进行定义
声明类成员
<?php
/**
* Define MyClass
*/
class MyClass
{
public $public = 'Public';
protected $protected = 'Protected';
private $private = 'Private';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj = new MyClass();
echo $obj->public; // 这行能被正常执行
echo $obj->protected; // 这行会产生一个致命错误
echo $obj->private; // 这行也会产生一个致命错误
$obj->printHello(); // 输出 Public、Protected 和 Private
/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// 可以对 public 和 protected 进行重定义,但 private 而不能
protected $protected = 'Protected2';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj2 = new MyClass2();
echo $obj->public; // 这行能被正常执行
echo $obj2->private; // 未定义 private
echo $obj2->protected; // 这行会产生一个致命错误
$obj2->printHello(); // 输出 Public、Protected2,但不会输出 Private
class Bar
{
public function test() {
$this->testPrivate();
$this->testPublic();
}
public function testPublic() {
echo "Bar::testPublic\n";
}
private function testPrivate() {
echo "Bar::testPrivate\n";
}
}
class Foo extends Bar
{
public function testPublic() {
echo "Foo::testPublic\n";
}
private function testPrivate() {
echo "Foo::testPrivate\n";
}
}
$myFoo = new foo();
$myFoo->test(); // Bar::testPrivate
// Foo::testPublic
?>
Note: 为了兼容性考虑,在 PHP 4 中使用 var 关键字对变量进行定义的方法在 PHP 5 中仍然有效(只是作为 public 关键字的一个别名)。在 PHP 5.1.3 之前的版本,该语法会产生一个
E_STRICT
警告。
类中的方法都必须使用关键字public、protected 或 private 进行定义。如果没有设置这些关键字,则该方法会被设置成默认的 public。
声明类中的方法
<?php
/**
* Define MyClass
*/
class MyClass
{
// 构造函数必须是 public
public function __construct() { }
// 声明一个 public 的方法
public function MyPublic() { }
// 声明一个 protected 的方法
protected function MyProtected() { }
// 声明一个 private 的方法
private function MyPrivate() { }
// 这个方法也是 public 的
function Foo()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate();
}
}
$myclass = new MyClass;
$myclass->MyPublic(); // 这行能被正常执行
$myclass->MyProtected(); // 这行会产生一个致命错误
$myclass->MyPrivate(); // 这行会产生一个致命错误
$myclass->Foo(); // Public、Protected 和 Private 都被调用了
/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// This is public
function Foo2()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate(); // 这行会产生一个致命错误
}
}
$myclass2 = new MyClass2;
$myclass2->MyPublic(); // 这行能被正常执行
$myclass2->Foo2(); // Public 和 Protected 都被调用了,但 Private 不会被调用
?>
继承已为大家所熟知的一个程序设计特性,PHP 的对象模型也使用了继承。继承将会影响到类与类,对象与对象之间的关系。
比如,当扩展一个类,子类就会继承父类的所有公有和保护方法。但是子类的方法会覆盖父类的方法。
继承对于功能的设计和抽象是非常有用的,而且对于类似的对象增加新功能就无须重新再写这些公用的功能。
继承代码示例
<?php
class foo
{
public function printItem($string)
{
echo 'Foo: ' . $string . PHP_EOL;
}
public function printPHP()
{
echo 'PHP is great.' . PHP_EOL;
}
}
class bar extends foo
{
public function printItem($string)
{
echo 'Bar: ' . $string . PHP_EOL;
}
}
$foo = new foo();
$bar = new bar();
$foo->printItem('baz'); // Output: 'Foo: baz'
$foo->printPHP(); // Output: 'PHP is great'
$bar->printItem('baz'); // Output: 'Bar: baz'
$bar->printPHP(); // Output: 'PHP is great'
?>
范围解析操作符(也可称作 Paamayim Nekudotayim)或者更简单地说是一对冒号,可以用于访问静态成员、方法和常量,还可以用于覆盖类中的成员和方法。
当在类的外部访问这些静态成员、方法和常量时,必须使用类的名字。
把 Paamayim Nekudotayim 选作该操作符的名字似乎有些奇怪。然而,这是 Zend 开发小组在写 Zend Engine 0.5 (被用于 PHP 3 中)时所作出的决定。事实上这个词在希伯莱文就是双冒号的意思。
在类的外部使用 :: 操作符
<?php
class MyClass {
const CONST_VALUE = 'A constant value';
}
echo MyClass::CONST_VALUE;
?>
self 和 parent这两个特殊的关键字是用于在类的内部对成员或方法进行访问的。
<?php
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();
?>
当一个子类覆盖其父类中的方法时,PHP 不会再执行父类中已被覆盖的方法,直到子类中调用这些方法为止。这种机制也作用于 构造函数和析构函数、重载 及 魔术 函数。
调用父类的方法
<?php
class MyClass
{
protected function myFunc() {
echo "MyClass::myFunc()\n";
}
}
class OtherClass extends MyClass
{
// 覆盖父类中的方法
public function myFunc()
{
// 但仍然可以调用已被覆盖的方法
parent::myFunc();
echo "OtherClass::myFunc()\n";
}
}
$class = new OtherClass();
$class->myFunc();
?>
声明类成员或方法为static,就可以不实例化类而直接访问。不能通过一个对象来访问其中的静态成员(静态方法除外)。
为了兼容PHP4,如果没有指定“可见性”,属性和方法默认为public。
由于静态方法不需要通过对象即可调用,所以伪变量$this在静态方法中不可用。
静态属性不可以由对象通过->操作符来访问。
用::方式调用一个非静态方法会导致一个E_STRICT级别的错误。
就像其它所有的PHP静态变量一样,静态属性只能被初始化为一个字符值或一个常量,不能使用表达式。 所以你可以把静态属性初始化为整型或数组,但不能指向另一个变量或函数返回值,也不能指向一个对象。
PHP5.3.0之后,我们可以用一个变量来动态调用类。但该变量的值不能为关键字self, parent 或static。
静态成员代码示例
<?php
class Foo
{
public static $my_static = 'foo';
public function staticValue() {
return self::$my_static;
}
}
class Bar extends Foo
{
public function fooStatic() {
return parent::$my_static;
}
}
print Foo::$my_static . "\n";
$foo = new Foo();
print $foo->staticValue() . "\n";
print $foo->my_static . "\n"; // Undefined "Property" my_static
print $foo::$my_static . "\n";
$classname = 'Foo';
print $classname::$my_static . "\n"; // PHP 5.3.0之后可以动态调用
print Bar::$my_static . "\n";
$bar = new Bar();
print $bar->fooStatic() . "\n";
?>
静态方法代码示例
<?php
class Foo {
public static function aStaticMethod() {
// ...
}
}
Foo::aStaticMethod();
$classname = 'Foo';
$classname::aStaticMethod(); // As of PHP 5.3.0
?>
PHP5支持抽象类和抽象方法。抽象类不能直接被实例化,你必须先继承该抽象类,然后再实例化子类。抽象类中 至少要包含一个抽象方法。如果类方法被声明为抽象的,那么其中就不能包括具体的功能实现。
继承一个抽象类的时候,子类必须实现抽象类中的所有抽象方法;另外,这些方法的可见性 必须和抽象类中一样(或者更为宽松)。如果抽象类中某个抽象方法被声明为protected,那么子类中实现的方法就应该声明为protected或者public,而不 能定义为private。
抽象类代码示例
<?php
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";
}
}
class ConcreteClass2 extends AbstractClass
{
public function getValue() {
return "ConcreteClass2";
}
public function prefixValue($prefix) {
return "{$prefix}ConcreteClass2";
}
}
$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";
$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') ."\n";
/* 输出:
ConcreteClass1
FOO_ConcreteClass1
ConcreteClass2
FOO_ConcreteClass2
*/
?>
使用接口(interface),你可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。
我们可以通过interface来定义一个接口,就像定义一个标准的类一样,但其中定义所有的方法都是空的。
接口中定义的所有方法都必须是public,这是接口的特性。
要实现一个接口,可以使用implements操作符。类中必须实现接口中定义的所有方法,否则会报一个fatal错误。如果要实现多个接口,可以用逗号来分隔多个接口的名称。
Note:
实现多个接口时,接口中的方法不能有重名。
Note:
接口也可以继承,通过使用extends操作符。
接口中也可以定义常量。接口常量和类常量的使用完全相同。它们都是定值,不能被子类或子接口修改。
<?php
// 声明一个'iTemplate'接口
interface iTemplate
{
public function setVariable($name, $var);
public function getHtml($template);
}
// 实现接口
// 下面的写法是正确的
class Template implements iTemplate
{
private $vars = array();
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
public function getHtml($template)
{
foreach($this->vars as $name => $value) {
$template = str_replace('{' . $name . '}', $value, $template);
}
return $template;
}
}
// 下面的写法是错误的,会报错:
// Fatal error: Class BadTemplate contains 1 abstract methods
// and must therefore be declared abstract (iTemplate::getHtml)
class BadTemplate implements iTemplate
{
private $vars = array();
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
}
?>
<?php
interface a
{
public function foo();
}
interface b extends a
{
public function baz(Baz $baz);
}
// 正确写法
class c implements b
{
public function foo()
{
}
public function baz(Baz $baz)
{
}
}
// 错误写法会导致一个fatal error
class d implements b
{
public function foo()
{
}
public function baz(Foo $foo)
{
}
}
?>
多个接口间的继承
<?php
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()
{
}
}
?>
使用接口常量
<?php
interface a
{
const b = 'Interface constant';
}
// 输出接口常量
echo a::b;
// 错误写法,因为常量的值不能被修改。接口常量的概念和类常量是一样的。
class b implements a
{
const b = 'Class constant';
}
?>
PHP所提供的"重载"(overloading)是指动态地"创建"类属性和方法。我们是通过 魔术方法(magic methods)来实现的。
当调用当前环境下未定义或不可见的类属性或方法时,重载方法会被调用。本节后面将使用 "不可访问属性(inaccessible properties)"和"不可访问方法(inaccessible methods)"来称呼这些未定义或不可见的类属性或方法。
所有的重载方法都必须被声明为public。
Note:
这些魔术方法的参数都不能 通过引用传递。
Note:
PHP中的"重载"与其它绝大多数面向对象语言不同。传统的"重载"是用于提供多个同名的类方法,但各方法的参数类型和个数不同。
版本 | 说明 |
---|---|
5.3.0 | 新增 __callStatic()魔术方法. 可见性未设置为public或未声明为static的时候会产生一个警告 |
5.1.0 | 新增 __isset() 和 __unset()两个魔术方法. |
$name
, mixed $value
)$name
)$name
)$name
)在给未定义的变量赋值时,__set() 会被调用。
读取未定义的变量的值时,__get() 会被调用。
当对未定义的变量调用isset() 或 empty()时,__isset()会被调用。
当对未定义的变量调用unset()时,__unset()会被调用。
参数$name是指要操作的变量名称。__set()方法的$value 参数指定了$name变量的值。
属性重载只能在对象中进行。在静态方法中,这些魔术方法将不会被调用。所以这些方法都不能被声明为static。从PHP 5.3.0起, 将这些魔术方法定义为static会产生一个警告。
Note:
因为PHP处理赋值运算的方式, __set() 的返回值将被忽略。 类似的, 在下面这样的链式赋值中, __get() 不会被调用:
$a = $obj->b = 8;
Note:
在除isset外的其它语言结构中无法使用重载的属性, 这意味着当对一个重载的属性 使用empty时,重载魔术方法将不会被调用。 (译注:此特性在新版本已改变,英文手册未更新)
为避开此限制,可以将重载属性赋值到本地变量再使用empty。
使用__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'\n";
$this->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;
}
/** PHP 5.1.0之后版本 */
public function __isset($name)
{
echo "Is '$name' set?\n";
return isset($this->data[$name]);
}
/** PHP 5.1.0之后版本 */
public function __unset($name)
{
echo "Unsetting '$name'\n";
unset($this->data[$name]);
}
/** 非魔术方法 */
public function getHidden()
{
return $this->hidden;
}
}
echo "<pre>\n";
$obj = new PropertyTest;
$obj->a = 1;
echo $obj->a . "\n\n";
var_dump(isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
echo "\n";
echo $obj->declared . "\n\n";
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";
echo "Privates not visible outside of class, so __get() is used...\n";
echo $obj->hidden . "\n";
/*输出:
Setting 'a' to '1'
Getting 'a'
1
Is 'a' set?
bool(true)
Unsetting 'a'
Is 'a' set?
bool(false)
1
Let's experiment with the private property named 'hidden':
Privates are visible inside the class, so __get() not used...
2
Privates not visible outside of class, so __get() is used...
Getting 'hidden'
Notice: Undefined property via __get(): hidden in <file> on line 70 in <file> on line 29
*/
?>
$name
, array $arguments
)$name
, array $arguments
)当调用一个不可访问方法(如未定义,或者不可见)时,__call() 会被调用。
当在静态方法中调用一个不可访问方法(如未定义,或者不可见)时,__callStatic() 会被调用。
$name参数是要调用的方法名称。$arguments参数是一个数组,包含着要传递给方法的参数。
使用 __call()和 __callStatic() 对方法重载
<?php
class MethodTest
{
public function __call($name, $arguments)
{
// 注意: $name 的值区分大小写
echo "Calling object method '$name' "
. implode(', ', $arguments). "\n";
}
/** PHP 5.3.0之后版本 */
public static function __callStatic($name, $arguments)
{
// 注意: $name 的值区分大小写
echo "Calling static method '$name' "
. implode(', ', $arguments). "\n";
}
}
$obj = new MethodTest;
$obj->runTest('in object context');
MethodTest::runTest('in static context'); // PHP 5.3.0之后版本
/* 输出
Calling object method 'runTest' in object context
Calling static method 'runTest' in static context
*/
?>
PHP5提供了一种迭代(iteration)对象的功能,就像使用数组那样,可以通过foreach来遍历对象中的属性。
默认情况下,在外部迭代只能得到外部可见的属性的值。
简单的对象迭代
<?php
class MyClass
{
public $var1 = 'value 1';
public $var2 = 'value 2';
public $var3 = 'value 3';
protected $protected = 'protected var';
private $private = 'private var';
function iterateVisible() {
echo "MyClass::iterateVisible:\n";
foreach($this as $key => $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
*/
?>
PHP 5自带的 Iterator接口来实现迭代. 使用Iterator接口可以让对象自行决定如何迭代自已。
实现Iterator接口的迭代
<?php
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";
}
/*输出:
rewinding
current: 1
valid: 1
current: 1
key: 0
0: 1
next: 2
current: 2
valid: 1
current: 2
key: 1
1: 2
next: 3
current: 3
valid: 1
current: 3
key: 2
2: 3
next:
current:
valid:
*/
?>
让类实现IteratorAggregate接口,这样你的类就不用强制性地实现 Iterator接口中的所有方法。
通过IteratorAggregate来实现对象迭代
<?php
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";
}
/*输出:
rewinding
current: value 1
valid: 1
current: value 1
key: 0
key/value: [0 -> value 1]
next: value 2
current: value 2
valid: 1
current: value 2
key: 1
key/value: [1 -> value 2]
next: value 3
current: value 3
valid: 1
current: value 3
key: 2
key/value: [2 -> value 3]
next:
current:
valid:
*/
?>
魔术方法:
__get
__set
__isset
__unset
继承,继承时使用的关键字,以及等级问题
还其的魔术方法和关键字
__get这个魔术方法,是在类的外部获得某对象的私有的成员属性的时候自动执行。必须要传入一个参数。
传进的这个参数为成员属性变量名的字符串。
__set 这个魔术方法,是修改对象当中私有的成员属性值的时候,自动调用。必须要传入两个参数,一个参数为成员属性名的字符串,另外一个参数为,成员属性需要修改为的值。
__isset 在类的外部检测某对象的成员属性是否存在的时候采用isset()方法进行检测或判断的时候,进行自动调用。必须传入一个参数。在类的内部根据逻辑isset()检测成员属性的值,然后return回去即可。
__unset 在类的外部unset()销毁对象的私有成员属性的时候,自动调用,必须要传入一个参数,这个参数为属性名。需要在类的这个方法当中unset($this->成员属性);
继承:
继承是个好东西。
继承能够极大的节约代码,提高重用性,方便维护,更加符合现实逻辑
格式:
Class 类名 extends 另外一个类。
父类当中的方法和子类当中的方法有共同部份的时候,要求父类当中的方法发生变化子类当中的方法也发生变化的时候,我们不重新再写一遍。
我们直接调用父类当中的方法到子类当中来执行,在子类当中补充一些业务逻辑即可。
Parent::父类当中的方法名
Private 只有自己能够知道,其他人都不能知道。
私有的不能在外部访问不能被修改,不能被删除。
私有的成员属性还不能被子类继承,只能在自己内部使用
Protected
只能被自己和子类修改和继承,但是不能被外部调用和修改
Public
公共的
不仅自己可以读,子类可以读,外部也可以读。
这几个关键词不仅能够修饰成员属性也可以修改成员方法,功能和作用一样。
方法/范围 | Private | Protected | public | var |
外部 | 不可以 | 不可以 | 可以 | 可以 |
子类 | 不可以 | 可以 | 可以 | 可以 |
自己当中 | 可以 | 可以 | 可以 | 可以 |
常用关键词:
Final
以final 开始的类,不能被继承。
以final 开头的方法,不能被重载
注意:final 可以与public等关键词一同使用
Static 关键词:
用static关键词修饰的成员属性和成员方法效率都很高,以后在结构上面准许的时候,尽可能的写成static的成员属性和成员方法
1,静态的成员属性在声明的时候,必须要加上static 关键字
2,静态的成员属性不能够 $this->静态成员属性 这样来调用
3,调用静态的成员属性的方法1 类名::静态变量
4,在类的内部调用静态的成员属性直接写上self::静态变量
5,调用静态的成员属性不需要初使化,直接 类名::静态变量,即可
6,静态属性前面也可以加上访问等级 public protected private,限制是否准许在外部访问,还是只能在类的内部来访问
7,静态变量是否可以继承:静态变量可以被继承。
静态的成员方法:
1, 静态的成员方法当中不能够有任何成态的成员属性。也就是说不能包含有以$this->打头的,任何属性和方法。
静态的成员方法,在类的内部调用时,可以直接 $this->静态方法名
1, 在类的内部调用静态的成员方法还可以 类名::静态方法名
2, 在类的内部调用静态的成员方法还可以 self::静态方法名
3, 类的外部可以直接$对象->静态方法()
4, 类的外部还可以,不需要实例化对象,直接类名::静态方法()
5, 静态方法可以加上其它关键字例如 final private public protected
Const
1,在类当中定义常量,必须要使用const 关键词
2,在类当中调用常量的时候,使用 类名::常量名
3, 在类当中调用常量的时候可以使用 self::常量名
4, 常量名通常大小
5,在类的外部调用时不需要实例化直接类名::常量名即可
__toString();
$t=new person;
echo $t;
因为$t是一个对象,它不能直接被echo ,所以上述代码会报错。
为了解决这个问题,我们可以在类当中写一个魔术方法__toString() ,不需要传入任何参数,在echo 对象的时候,自动调用。
用了__tostring之后,就必须要在toString当中返回一个字符串
Echo 对象的时候,就是显示的return返回的字符串值。
注意:如果方法名没有写等级默认等级为public,以后再写代码的时候,注意把等级都需要加上。
Private 不需要继承,不准外部访问就用private
Protected 需要继承的时候,就用protected
Public 准许外部访问的时候就用public