PHP学习之四:类与对象


1 基础

先看一个简单的类的声明

<?php
class SimpleClass
{
    
    public $a;
    private $b = 0;
    protected $c;

    public function getA()
    {
         return $this->a;
    }
}

$obj = new SimpleClass();

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

?>

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

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

<?php
class Test
{
    static public function getNew()
    {
        return new static;
    }
}

class Child extends Test
{}

$obj1 = new Test();
$obj2 = new $obj1;
var_dump($obj1 !== $obj2);  //true

$obj3 = Test::getNew();
var_dump($obj3 instanceof Test);  //true

$obj4 = Child::getNew();
var_dump($obj4 instanceof Child); //true
?>

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


2 类成员

类成员可以由public、private和protected后加上正常的变量声明即可。成员变量可以直接初始化,但只能初始化为常量。

在类的上下文,非静态的成员变量以$this->property的方式来访问。静态成员变量只能以self::$property的方式来访问。

<?php
class test
{
    public $var1 = 1;
    protected $var2 = 2;
    private $var3 = 3;
    static $var4 = 4;
   
    public function toArray()
    {
        return (array) $this;
    }
}

$t = new test;
print_r($t->toArray());

/* outputs:
Array
(
    [var1] => 1
    [ * var2] => 2
    [ test var3] => 3
)

*/
?> 


3 类常量

类中可以定义常量,常量不能以$开头,且必须为常量表达式,而非变量、成员属性或函数调用。

类常量的访问与类静态成员类似,只是不需要以$开头。

<?php
class MyClass
{
    const constant = 'constant value';

    function showConstant() {
        echo  self::constant . "\n";
    }
}

echo MyClass::constant . "\n";


4 自动加载类

当在不同的文件中实现了多个类时,需要在使用这些类的时候包含这些文件,这需要在文件前面写很多include或require。为了解决这个问题PHP5.3定义了__autoload函数,当需要使用某个类时,可以通过此函数提供的实时加载功能来完成类文件的包含。当然这需要你以一定的方式来命令类对应的文件名。

<?php
function __autoload($class_name) {
    include $class_name . '.php';
}

$obj  = new MyClass1();  //包含MyClass1.php
$obj2 = new MyClass2(); //包含MyClass2.php
?>

5 构造与析构

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

<?php
class BaseClass {
   function __construct() {
       print "In BaseClass constructor\n";
   }
    function __destruct()
    {
         print "In BaseClass destructor\n";
    }
}

class SubClass extends BaseClass {
   function __construct() {
       parent::__construct();
       print "In SubClass constructor\n";
   }

    function __destruct()
    {
        print "in SubClass destructor\n";
         parent::__destruct();
    }
}

$obj = new BaseClass();
$obj = new SubClass();
?>

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


6 范围解析::

范围解析提供允许我们访问类的静态变量、类常量或类的重载方法。关键词self,parent以及static允许我们在类定义的内部访问类的方法和属性。

self和parent的用法见上面的代码即可。

7 static关键字

类的静态方法和静态成员可以不需要类实例而进行访问。静态成员不能通过$this伪变量访问,同时在静态方法的内部$this变量是无效的。

类的静态成员变量只能被初始化为字面常量。表达式是不允许的。

8 抽象类

PHP引入了抽象类与抽象方法的概念。与C++类似,抽象类不能被实例化,包含抽象方法的类必须是抽象类,抽象方法不能在有实现部分。

对于抽象类的派生类,必须实现所有抽象方法且定义为相同的可见性。

<?php
abstract class AbstractClass
{
    // Force Extending class to define this method
    abstract protected function getValue();
    abstract protected function prefixValue($prefix);

    // Common method
    public function printOut() {
        print $this->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的用法。

<?php
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();
?>
//output : Hello World! 

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

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

<?php
trait A {
    public function smallTalk() {
        echo 'a';
    }
    public function bigTalk() {
        echo 'A';
    }
}

trait B {
    public function smallTalk() {
        echo 'b';
    }
    public function bigTalk() {
        echo 'B';
    }
}

class Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
    }
}

class Aliased_Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
        B::bigTalk as talk;
    }
}

//trait组合的方法示例
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一个不可访问的属性时调用

属性拦截只能在对象上下文中工作,不能在静态上下文中工作。需要注意的属性拦截器会破坏原始类的封装性,因为当访问一个私有属性时也会通过拦截器返回某些东西。

<?php
class PropertyTest
{
    /**  Location for overloaded data.  */
    private $data = array();

    /**  Overloading not used on declared properties.  */
    public $declared = 1;

    /**  Overloading only used on this when accessed outside the class.  */
    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;
    }

    /**  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 "<pre>\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 <file> on line 70 in <file> on line 29
?>


11.2 方法拦截器

public mixed __call(string $name,array $args)                      当访问不可见方法时调用

public static mixed __callStatic(string $name, array $args);   同上,用于静态上下文中

<?php
class MethodTest
{
    public function __call($name, $arguments)
    {
        // Note: value of $name is case sensitive.
        echo "Calling object method '$name' "
             . implode(', ', $arguments). "\n";
    }

    /**  As of PHP 5.3.0  */
    public static function __callStatic($name, $arguments)
    {
        // Note: value of $name is case sensitive.
        echo "Calling static method '$name' "
             . implode(', ', $arguments). "\n";
    }
}

$obj = new MethodTest;
$obj->runTest('in object context');

MethodTest::runTest('in static context');  // As of PHP 5.3.0
?>

12 对象遍历

对象可以看做一个数组,通过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
对象也可以实现Iterator接口,来自行控制如何输出内容。

13 魔法方法

除了前面见到的以__开头的方法,下面这些方法 __sleep()  __wakeup()  __toString()  __invoke()  __set_state()  __clone() 也是魔法方法。

public array __sleep()      当调用serialize()时,会首先调用定义的__sleep()方法,其返回类所有需要序列化的变量名,若方法没有返回数据,则NULL被序列化。

void __wakeup()              当调用unserialize()时,会检查__wakeup()并调用。

<?php
class Connection
{
    protected $link;
    private $server, $username, $password, $db;
    
    public function __construct($server, $username, $password, $db)
    {
        $this->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([$...])       当直接调用一个对象时,会查找类定义的此方法并调用之。

<?php
class CallableClass
{
    public function __invoke($x)
    {
        var_dump($x);
    }
}
$obj = new CallableClass;
$obj(5);
var_dump(is_callable($obj));
?>

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

<?php

class A
{
    public $var1;
    public $var2;

    public static function __set_state($an_array) // As of PHP 5.1.0
    {
        $obj = new A;
        $obj->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 )

<?php
class SubObject
{
    static $instances = 0;
    public $instance;

    public function __construct() {
        $this->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__对当前类的引用,取决当前方法所在类范围。

<?php
class base
{
    static public function who()
    {
          print "base\n";
    }

    static public function call()
    {
        static::who();   //输出 child
        self::who();     //输出 base
    }
}

class child extends base
{
    static public function who()
    {
        echo "child\n";
    }
}


19 对象与引用

通过下面的例子来看一下PHP中的对象默认以引用传递的含意。

<?php
class A {
    public $foo = 1;
}  

$a = new A;
$b = $a;     // $a and $b are copies of the same identifier
             // ($a) = ($b) = <id>
$b->foo = 2;
echo $a->foo."\n";    //2

$c = new A;
$d = &$c;    // $c and $d are references
             // ($c,$d) = <id>

$d->foo = 2;
echo $c->foo."\n";   //2

$e = new A;
function foo($obj) {
    // ($obj) = ($e) = <id>
    $obj->foo = 2;
}

foo($e);
echo $e->foo."\n";   //2
?>


20 对象序列化

PHP对象都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialinze()函数能把字符串重新变回PHP对象。序列化一个对象只会保存对象的所有变量,不会保存对象的方法。

为了能unserialize()一个对象,对象的类定义必须有。要想在另一个文件unserialize()对象,可以通过包含定义此类的文件或使用spl_autoload_register()来实现。

<?php
// classa.inc:
  
  class A {
      public $one = 1;
    
      public function show_one() {
          echo $this->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();
?>



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值