PHP 类与对象

命名空间

什么是命名空间呢?

命名空间(英语:Namespace)表示标识符(identifier)的可见范围。一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。这样,在一个新的命名空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其它命名空间中。

简单说来命名空间是为了解决不同库在同一个环境下使用时出现的命名冲突。例如我自己有一个函数名叫A,但是系统已经有一个A函数了,那就会出现冲突。

类与对象


澳门新葡亰娱乐在线,PHP中的命名空间

PHP从5.3版本开始引入了命名空间,之后许多现有PHP类库以及框架都开始支持它。那么PHP的命名空间怎么用呢?

基本概念

每个类的定义都是关键class开头。

<?php
namespace NS
{
    class A
    {
        // 成员属性
        public $temp = '成员属性';

        // 方法
        public function displayVar()
        {
            // 使用伪变量$this调用成员属性
            echo $this->temp . "<br>";
        }
    }
// 使用new关键字实例化一个类
    $temp = new A();
    echo $temp->displayVar();

    class B extends A
    {
        // 构造方法
        function __construct()
        {
            $this->temp = '成员属性B继承于A';
        }
    }
    $temp = new B();
    echo $temp->displayVar();

    echo A::class;
}
?>

定义命名空间

下面是一个定义命名空间的例子

<?php    
//file a.php
namespace A;

const test = 'Atest'; 

function test() { 
    return __FUNCTION__; 
}

class Test{
    public function __construct(){
        return __METHOD__;
    }
}
?>

上面例子中就是一个典型的命名空间定义方法,只有constfunction,class受命名空间的约束。

访问控制

public(公有),protected(受保护)或 private(私有)。

使用命名空间

使用命名空间的方式如下:

<?php 
namespace B;
use A;

const test = 'Btest';
function test() { 
    return __FUNCTION__; 
}

class Test{
    public function __construct(){
        return __METHOD__;
    }
}

include "a.php";//必须包含A命名空间的文件

// 完全限定
// `Btest`从绝对位置查找输出,如果是全局函数则`test`
echo Btest;   //输出Btest

// 限定名称  
// 这里已经通过`use A`申明了在这个文件可以通过`A...`使用A命名空间的函数
echo Atest;    //输出Atest

// 非限定名称
// 非限定名称的函数`test`会从当前命名控件查找,即B
echo test;      //输出Btest

// namespace关键字代表当前命名空间
echo namespace/test;
?>

首先要注意的是命名空间只起申明作用,也就是在使用了命名空间的时候依然得把这个命名空间申明的那个文件包含进来。在使用中可以通过__NAMESPACE__来查看当前命名空间。

更多内容可以查看PHP官方文档

成员变量使用

可以用 ->(对象运算符):$this->property(其中 property
是该属性名)这种方式来访问非静态属性。 静态属性则是用
::(双冒号):self::$property 来访问。

自动加载

每个文件既要申明命名控件又要手动include是非常不智能的事情,所以在自己的PHP系统或者框架中可以使用自动加载技术,让系统自己去找

最简单的方式是利用函数__autoload函数,但是这个函数只能在非命名控件下定义,也就是全局情况下:

function __autoload($class) {
  $dir = './';
  set_include_path(get_include_path().PATH_SEPARATOR.$ids_dir);
  $class = str_replace('\', '/', $class) . '.php'; 
  require_once($class); }

如果在已经有了命名空间的类中,可以使用函数spl_autoload_register来注册一个类中的方法来代替__autoload

类常量

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

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

echo MyClass::constant . "n";

$classname = "MyClass";
echo $classname::constant . "n"; // 自 5.3.0 起

$class = new MyClass();
$class->showConstant();

echo $class::constant."n"; // 自 PHP 5.3.0 起
?>

类的自动加载

spl_autoload_register()
函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载。

本例尝试分别从 MyClass1.php 和 MyClass2.php 文件中加载 MyClass1 和
MyClass2 类。

<?php
spl_autoload_register(function ($class_name) {
    require_once $class_name . '.php';
});

$obj  = new MyClass1();
$obj2 = new MyClass2();
?>

构造函数

<?php
class BaseClass {
   function __construct() {
       print "In BaseClass constructorn";
   }
}

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

class OtherSubClass extends BaseClass {
    // inherits BaseClass's constructor
}

// In BaseClass constructor
$obj = new BaseClass();

// In BaseClass constructor
// In SubClass constructor
$obj = new SubClass();

// In BaseClass constructor
$obj = new OtherSubClass();
?>

析构函数

<?php
class MyDestructableClass {
   function __construct() {
       print "In constructorn";
       $this->name = "MyDestructableClass";
   }

   function __destruct() {
       print "Destroying " . $this->name . "n";
   }
}

$obj = new MyDestructableClass();
?>

Static(静态)关键字

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

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

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

抽象类

任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。

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

对象接口

使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。

接口是通过 interface
关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的。

接口中定义的所有方法都必须是公有,这是接口的特性。

实现(implements)

要实现一个接口,使用 implements
操作符。类中必须实现接口中定义的所有方法,否则会报一个致命错误。
类可以实现多个接口,用逗号来分隔多个接口的名称。

Trait代码复用

<?php

class A
{
    public function sayHello()
    {
        echo "Hello ";
    }
}

trait mytrait
{
    public function traitFunction()
    {
        parent::sayHello();
        echo "world!";
    }

}

class B extends A
{
    use mytrait;
}

$temp = new B();
$temp->traitFunction();

匿名类

PHP 7 开始支持匿名类。

<?php

// PHP 7 之前的代码
class Logger
{
    public function log($msg)
    {
        echo $msg;
    }
}

$util->setLogger(new Logger());

// 使用了 PHP 7+ 后的代码
$util->setLogger(new class {
    public function log($msg)
    {
        echo $msg;
    }
});

魔术方法

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 会产生一个警告。

遍历对象

PHP 5 提供了一种定义对象的方法使其可以通过单元列表来遍历, 例如用
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:<br>";
        foreach ($this as $key => $value)
        {
            print '$key=>' . $key . "<br>";
            print '$value=>' . $value . "<br>";
        }
    }
}

$class = new MyClass();
$class->iterateVisible();

foreach ($class as $key => $value)
{
    print '$key=>' . $key . "<br>";
    print '$value=>' . $value . "<br>";
}

Final 关键字

PHP 5 新增了一个 final 关键字。如果父类中的方法被声明为
final,则子类无法覆盖该方法。 如果一个类被声明为 final,则不能被继承。

<?php

class BaseClass
{
    public function display()
    {
        echo 'final';
    }

    final public function test()
    {
        echo 'Hello World!';
    }
}

class A extends BaseClass
{
    public function display()
    {
        echo 'A';
    }

    public function test()
    {
        echo 'haha';
    }
}

$temp = new A();
$temp->test();
#会报错,final方法不能重写

对象复制

在多数情况下,我们并不需要完全复制一个对象来获得其中属性。但有一个情况下确实需要:如果你有一个
GTK 窗口对象,该对象持有窗口相关的资源。

你可能会想复制一个新的窗口,保持所有属性与原来的窗口相同,但必须是一个新的对象(因为如果不是新的对象,那么一个窗口中的改变就会影响到另一个窗口)。

还有一种情况:

如果对象 A 中保存着对象 B 的引用,当你复制对象 A
时,你想其中使用的对象不再是对象 B 而是 B 的一个副本,那么你必须得到对象
A 的一个副本。

对象比较

当使用比较运算符(==)比较两个对象变量时,比较的原则是:

如果两个对象的属性和属性值
都相等,而且两个对象是同一个类的实例,那么这两个对象变量相等。

而如果使用全等运算符(===),这两个对象变量一定要指向某个类的同一个实例(即同一个对象)。

类型约束

PHP 5 可以使用类型约束。

如果一个类或接口指定了类型约束,则其所有的子类或实现也都如此。

类型约束不能用于标量类型如 int 或 string。Traits 也不允许。

<?php

class MyClass
{
    public function test_class(OtherClass $otherclass)
    {
        echo $otherclass->var;
        echo "<br>";
    }

    public function test_array(array $input_array)
    {
        print_r($input_array);
    }
}

class OtherClass
{
    public $var = 'Hello World!';
}

$myclass    = new MyClass();
$otherclass = new OtherClass();
$myclass->test_class($otherclass);

$input_array = ['one' => 'first', 'two' => 'second'];
$myclass->test_array($input_array);

后期静态绑定

自 PHP 5.3.0 起,PHP
增加了一个叫做后期静态绑定的功能,用于在继承范围内引用静态调用的类。

self:: 的限制

使用 self::
或者 CLASS 对当前类的静态引用,取决于定义当前方法所在的类:

后期静态绑定的用法

<?php

class A
{
    public static function who()
    {
        echo __CLASS__;
    }
    public static function test()
    {
//        self::who(); // 输出A
        static::who(); // 输出B
    }
}

class B extends A
{
    public static function who()
    {
        echo __CLASS__;
    }
}

$temp = new B();
$temp::test();

对象和引用

在php5
的对象编程经常提到的一个关键点是“默认情况下对象是通过引用传递的”。

但其实这不是完全正确的。下面通过一些例子来说明。

PHP 的引用是别名,就是两个不同的变量名字指向相同的内容。 在 PHP
5,一个对象变量已经不再保存整个对象的值。只是保存一个标识符来访问真正的对象内容。
当对象作为参数传递,作为结果返回,或者赋值给另外一个变量,另外一个变量跟原来的不是引用的关系,
只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容。

<?php

class A
{
    public $foo = 1;

}

$a = new A();
$b = $a;

$b->foo = 2;
echo $a->foo . "n"; // 输出为2

对象序列化

序列化对象 – 在会话中存放对象

所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。
unserialize()函数能够重新把字符串变回php原来的值。
序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。

<?php
//
//include("class.inc");
//
//$a = new A();
//$a->show_one();
//
//$s = serialize($a);
//file_put_contents('store', $s);

// 反序列化必须要包含此文件。
include("class.inc");
$s = file_get_contents('store');
$a = unserialize($s);

$a->show_one();

命名空间

  1. 解决PHP内部的类、函数、常量或第三方类、函数、常量之间的名字冲突。
  2. 为很长的标识名称创建一个别名,提高源代码的可读性。

<?php

namespace myname;

class MyClass{}

function myfunction(){}

const MYCONST = 1;

$a = new MyClass();
$c = new mynameMyClass;
$a = strlen('hi');
$d = namespaceMYCONST;
$d = __NAMESPACE__ . 'MYCONST';
echo $d;
echo constant($d);

定义命名空间

虽然任意合法的PHP代码都可以包含在命名空间中,但只有以下类型的代码受命名空间的影响,它们是:

类、接口、函数和常量。

命名空间通过关键字namespace来声明。如果一个文件中包含命名空间,它必须在其它代码之前声明命名空间,
有一个除外:declare关键字。

<?php

namespace spaceMyProject;

const CONNECT_OK = 1;

class Connection{}
function connect(){}

echo __NAMESPACE__;

发表评论

电子邮件地址不会被公开。 必填项已用*标注

相关文章

网站地图xml地图