类与对象之重载和命名空间的概念
一、重载
PHP提供的“重载”是指动态的“创建”类属性和方法。是通过魔术方法来实现的。
当调用当前环境下未定义或不可见的类属性或方法时,重载方法会被调用。
所有的重载方法都必须被声明为 public。
1.1 属性重载
public __set ( string $name , mixed $value ) : void
public __get ( string $name ) : mixed
public __isset ( string $name ) : bool
public __unset ( string $name ) : void
在给不可访问属性赋值时,__set() 会被调用。
读取不可访问属性的值时,__get() 会被调用。
当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。
当对不可访问属性调用 unset() 时,__unset() 会被调用。
参数$name
是指要操作的变量名称。__set()
方法的$value
参数指定了$name
变量的值。
属性重载只能在对象中进行。在静态方法中这些魔术方法是无效的。因此,这些魔术方法都不能声明为static
。
- 示例:使用
__get()
,__set()
,__isset()
和__unset()
进行属性重载
class Demo
{
// 被重载的数据保存在该数组中
protected $data = [];
// 公有属性
public $age = 31;
// 在给不可访问属性赋值时,__set() 会被调用。
public function __set($name, $value)
{
echo "Set Property ` $name ` valued ` $value `";
return $this->data[$name] = $value;
}
// 读取不可访问属性的值时,__get() 会被调用。
public function __get($name)
{
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
}
// 当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。
public function __isset($name)
{
if (!isset($this->data[$name])) {
echo "$name has not been set. <br>";
} else {
echo "$name has been set. <br>";
}
return isset($this->data[$name]);
}
// 当对不可访问属性调用 unset() 时,__unset() 会被调用。
public function __unset($name)
{
echo "$name has been unset.";
unset($this->data[$name]);
}
}
// 实例化Demo类
$obj = new Demo;
// 给不可访问属性赋值
$obj->username = '残破的蛋蛋';
echo '<br>';
// 访问一个属性
echo $obj->username; // 残破的蛋蛋
echo '<br>';
// 对不可访问属性调用isset()
var_dump(isset($obj->username)); // username has been set. bool(true)
echo '<br>';
// 当对不可访问属性调用 unset()
unset($obj->username); // username has been unset.
echo '<br>';
// 再次对不可访问属性使用isset()检测是否还存在该属性
var_dump(isset($obj->username)); // username has not been set. bool(false)
// 重载不能用于已经定义的属性上
echo $obj->age; // 31
注意:重载不能用在已经被定义的属性上。
1.2 方法重载
在对象中调用一个不可访问方法时,__call()
会被调用。
在静态上下文中调用一个不可访问方法时,__callStatic()
会被调用。
$name
参数是要调用的方法名称。$arguments
参数是一个枚举数组,包含着要传递给方法$name
的参数。
- 语法示例
public __call ( string $name , array $arguments ) : mixed
public static __callStatic ( string $name , array $arguments ) : mixed
其中__callStatic的版本要求在PHP 5.3.0+。
- 示例:使用
__call()
和__callStatic()
对方法重载
class Demo
{
// 方法重载
// 在对象中调用一个不可访问方法时,` __call() `会被调用。
public function __call($name, $arguments)
{
$len = count($arguments);
echo "Calling object method $name contains $len arguments.";
}
// 在静态上下文中调用一个不可访问方法时,` __callStatic() `会被调用。
public static function __callStatic($name, $arguments)
{
$len = count($arguments);
echo "Calling static method $name contains $len arguments.";
}
}
// 实例化Demo类
$obj = new Demo;
// 方法重载
$obj->getName('残破的蛋蛋', '男', 18); // Calling object method getName contains 3 arguments.
echo '<br>';
Demo::getUserInfo('拤碎的蛋蛋', '女', 31); // Calling static method getUserInfo contains 3 arguments.
二、命名空间
2.1 命名空间的概念
什么是命名空间?从广义上来说,命名空间是一种封装事物的方法。在很多地方都可以见到这种抽象概念。例如,在操作系统中目录用来将相关文件分组,对于目录中的文件来说,它就扮演了命名空间的角色。
在 PHP 中,命名空间用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题:
- 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
- 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。
PHP 命名空间 提供了一种将相关的类、函数和常量组合到一起的途径。
2.2 命名空间的定义
2.2.1 命名空间的声明
命名空间通过关键字namespace
来声明。并且必须在PHP文件的最前面声明命名空间(declare除外)。
- 语法示例:
namespace ns;
// 类
class Model
{
// TODO
}
// 常量
const PATH = '/models/';
// 函数
function set()
{
// TODO
}
如果按照下面的方法声明命名空间,将会报致命错误。
<html>
<?php
namespace ns; // 致命错误 - 命名空间必须是程序脚本的第一条语句。
?>
- 上述代码将会报错:
2.2.2 命名空间的成员的访问
PHP命名空间成员的访问类似于根据文件的路径访问某个文件,比如:在电脑的E盘下的www
文件夹有一个index.php
文件,那么我们访问这个文件的路径就是E:/www/index.php
。
namespace ns1 {
// 类
class Model
{
//....
}
// 常量
const PATH = '/models/';
// 函数
function set()
{
//...
}
echo Model::class . "<br>"; // ns1\Model
echo PATH::class . "<br>"; // ns1\PATH
echo set::class . "<hr>"; // ns1\set
}
通常,我们为了防止一个空间中的代码过大,可以将同一个空间的代码写到多个脚本中去。
例如:有一个PHP文件下有一个名为ns
的命名空间,由于该文件过大,现在将其拆分成两个单独的脚本文件,demo1.php
和demo2.php
。
- demo1.php文件
namespace ns {
class Demo1
{
// 假设这里有500行代码
public function __construct()
{
echo 1;
}
}
}
- demo2.php文件
namespace ns {
class Demo2
{
// 假设这里有800行代码
public function __construct()
{
}
}
}
- 现在我们在
demo.php
脚本中引用这两个文件
namespace ns;
require 'demo1.php';
require 'demo2.php';
echo Demo1::class, '<br>'; // ns\Demo1
echo Demo2::class, '<br>'; // ns\Demo2
2.2.3 子命名空间
与目录和文件的关系很象,PHP 命名空间也允许指定层次化的命名空间的名称。因此,命名空间的名字可以使用分层次的方式定义:
// 父空间
namespace ns1
{
class Demo
{
# TODO
}
echo Demo::class,'<br>'; // ns1\Demo
// 访问它的子空间成员
// namespace:用在空间中,表示当前空间的引用,类似于$this,self
// echo namespace\ns2\Demo::class,'<br>'; // ns1\ns2\Demo
echo ns2\Demo::class,'<br>'; // ns1\ns2\Demo
echo ns2\ns3\Demo::class, '<hr>'; // ns1\ns2\ns3\Demo
}
// ns1的子空间
namespace ns1\ns2
{
class Demo
{
# TODO
}
// echo __NAMESPACE__;
echo Demo::class,'<br>'; // ns1\ns2\Demo
echo ns1\ns2\ns3\Demo::class,'<hr>';
}
// ns2的子空间
namespace ns1\ns2\ns3
{
class Demo
{
# TODO
}
echo Demo::class,'<br>'; // ns1\ns2\ns3\Demo
// 在ns3中访问上级空间应该怎么办?
echo \ns1\ns2\Demo::class, '<br>'; // ns1\ns2\Demo
echo \ns1\Demo::class, '<br>'; // ns1\Demo
// 全局成员:前面加上全局空间的标识符:\
echo \Demo::class; // Demo
}
namespace
{
class Demo
{
# TODO
}
}