PHP学习之四:类与对象


1 基础

先看一个简单的类的声明

a;
    }
}

$obj = new SimpleClass();

$name = "SimpleClass";
$obj = new $name();

?>

对象的创建由new完成,new可以使用类名,或者是类名的字符串变量来生成对象。在类的上下文中,可以通过new self和new parent来创建对象。

当把一个变量指向的对象赋值给新的变量时,新旧变量访问的是同一个对象实例。若需要创建对象复本,需要使用clone来进行。当然我们可以把类实现成工厂,通过一个静态接口返回新的对象。PHP5.3支持下面的方法来返回一个新的对象实例。



PHP和JAVA一样,使用extends来实现继承,不支持多重继承,一个类只能有一个基类,可以通过implements实现多个接口。


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 构造与析构

对于构造函数,在派生的情况下,需要由子类显式的调用基类的构造函数。对于析构,也是相同的情况。



在继承时,子类自动拥有父类的public和protected成员,除非子类对父类方法进行了覆盖。


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";
?>

9 接口

接口定义了必须实现的方法集,接口中的方法为抽象且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! 

需要注意的是在use一个traits时,会存在覆盖的可能。traits的方法会覆盖基类中的同名函数,而使用traits的类若在后面实现了新的同名函数,则覆盖traits的同名函数。

若多个traits实现了同名函数,则使用这些traits的类会出现命名冲突,为了解决冲突需要使用insteadof..as...关键词。下面的代码使用A的bigTalk以及By的smalTalk方法。

trait HelloWorld {
    use HelloWorld;
}
?>
此外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
?>

12 对象遍历

对象可以看做一个数组,通过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([$...])       当直接调用一个对象时,会查找类定义的此方法并调用之。


static object __set_state()   当调用var_export()时,会查找类定义的此方法并调用之。

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();
?>



你可能感兴趣的:(Web开发)