PHP高级 面向对象2 第31天

2012-11-15 17:35:20 0  category: 第二阶段php


访问控制


对属性或方法的访问控制,是通过在前面添加关键字 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;
?>



selfparent这两个特殊的关键字是用于在类的内部对成员或方法进行访问的。  



<?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关键字



声明类成员或方法为static,就可以不实例化类而直接访问。不能通过一个对象来访问其中的静态成员(静态方法除外)。  


  为了兼容PHP4,如果没有指定“可见性”,属性和方法默认为public。  

由于静态方法不需要通过对象即可调用,所以伪变量$this在静态方法中不可用。  


 静态属性不可以由对象通过->操作符来访问。  


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


  就像其它所有的PHP静态变量一样,静态属性只能被初始化为一个字符值或一个常量,不能使用表达式。   所以你可以把静态属性初始化为整型或数组,但不能指向另一个变量或函数返回值,也不能指向一个对象。  


PHP5.3.0之后,我们可以用一个变量来动态调用类。但该变量的值不能为关键字self,   parentstatic



静态成员代码示例



<?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()两个魔术方法.



属性重载

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变量的值。  


属性重载只能在对象中进行。在静态方法中,这些魔术方法将不会被调用。所以这些方法都不能被声明为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


*/
?>

方法重载

public mixed __call     ( string $name    , array $arguments    )
public static mixed __callStatic     ( string $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:

*/
?>


面向对象2


魔术方法:

__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