魔术方法(魔术函数)
类内部使用的方法
__construct()
:实例化对象时被调用;__destuct()
:当删除一个对象或者对象销毁前是被执行;
__get()
:调用对象不存在的属性或没有声明的属性值被调用;
__set()
:给一个未定义的属性或没有声明的属性赋值时调用此方法;
__call()
:调用对象不存在方法时被调用;
这里的没有声明包括当使用对象调用时,访问控制修饰符为proteced或private的属性(即没有权限访问的属性)。
<?php
class Human {
//定义属性
public $name = "Jack"; //姓名属性
public $sex = "female"; //性别属性
public $age = 23; //年龄属性
var $addr; //通讯属性 PHP4里面声明属性
public $data = array(); //定义data属性 用来存放数据
//定义方法
public function reading() {
}
public function talking() {
echo "Hellow world!<br />";
}
//PHPdocuments
function html() {//how to make love
}
/**
* 魔术方法 当满足特定场景时 自动调用的方法
* @param string $name 要设置的未定义属性名称
* @param mixed $value 设置的值
*/
public function __set($name, $value) {
// echo "你正在尝试给一个未定义的属性{$name}赋值!{$value}";
//将未定义的属性和值 以键值对 存入data属性中 在类的内部使用$this指代当前调用对象 访问属性和方法
$this->data[$name] = $value;
}
//读取一个未定义的属性时 自动调用的魔术方法
public function __get($name) {
// echo "你正在读取未定义的属性{$name}";
echo $this->data[$name];
// return $this->data[$name];
}
//访问未定义的方法时 自动调用的魔术方法
public function __call($name, $arguments) {
// echo $name;
// var_dump($arguments);
echo "你调用了一个未定义的方法:{$name},并且携带参数:";
var_dump($arguments);
}
}
//实例化一个对象
$Jack = new Human();
//通过对象去获取属性和方法 通过"->"运算符 .
echo $Jack->name."的年龄是:".$Jack->age."<br />";
$Jack->talking();
//给一个未定义的属性赋值时 会调用魔术方法 __set();
$Jack->from = "USA";
var_dump($Jack->data);
echo $Jack->from."<br />";
//访问未定义的方法
$Jack->abc("1", "a");
__callStatic()
__callStatic()
,用静态方式中调用一个不可访问方法时调用
class Person
{
function say()
{
echo "Hello, world!<br>";
}
/**
* 声明此方法用来处理调用对象中不存在的方法
*/
public static function __callStatic($funName, $arguments)
{
echo "你所调用的静态方法:" . $funName . "(参数:" ; // 输出调用不存在的方法名
print_r($arguments); // 输出调用不存在的方法时的参数列表
echo ")不存在!<br>\n"; // 结束换行
}
}
$Person = new Person();
$Person::run("teacher"); // 调用对象中不存在的方法,则自动调用了对象中的__call()方法
$Person::eat("小明", "苹果");
$Person->say();
_toString()
_toString()
:打印一个对象时被调用,比如echo $obj ,print($obj)
。__toString()
,类被当成字符串时的回应方法此方法必须返回一个字符串,否则报错或500
。不能在 __toString() 方法中抛出异常。这么做会导致致命错误。
<?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
public function __toString()
{
return 'go go go';
}
}
$person = new Person('小明'); // 初始赋值
echo $person;
__clone()
__clone()
:克隆对象时被调用,比如$t = new Test()
,$tt = clone $t;
在单例模式中已经介绍
__sleep()
__sleep()
:如过存在此方法serialize之前被调用
,此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。
注意:
__sleep()
不能返回父类的私有成员的名字。这样做会产生一个 E_NOTICE 级别的错误。可以用 Serializable 接口来替代。作用:__sleep()
方法常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,这个功能就很好用。
__wakeup()
__wakeup():unserialize之前被调用,__wakeup() 经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。但不需要全部保存,这个功能就很好用。
与serialize相反,unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
<?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
public function __sleep() {
echo "当在类外部使用serialize()时会调用这里的__sleep()方法<br>";
$this->name = base64_encode($this->name);
return array('name', 'age'); // 这里必须返回一个数值,里边的元素表示返回的属性名称
}
public function __wakeup() {
echo "当在类外部使用unserialize()时会调用这里的__wakeup()方法<br>";
$this->name = 2;
$this->sex = '男';
// 这里不需要返回数组
}
}
$person = new Person('小明'); // 初始赋值
var_dump(serialize($person));
var_dump(unserialize(serialize($person)));
__isset和__unset()
__isset($property)
:当对一个未定义的属性或者没有生命的属性调用isset()函数时调用此方法,如 isset($c->name);
__unset()
:当对一个未定义的属性或没有声明的属性调用unset()函数时调用此方法,如:unset($c->name);
这里的没有声明包括当使用对象调用时,访问控制修饰符为proteced
或private
的属性(即没有权限访问的属性)。
<?php
class Human {
public $name;
private $sex;
public function __isset($name) {
echo "__isset被调用了:属性$name<br/>";
}
function __unset($name)
{
echo "__unset被调用了:属性$name<br/>";
}
}
//实例化对象
$test = new Human();
isset($test->sex);//调用私有属性 被自动调用了
isset($test->age);//调用未定义的属性 被自动调用了
echo "<br/>";
unset($test->sex);//删除私有属性 被自动调用了
unset($test->age);//输出未定义的属性 被自动调用了
__invoke()
__invoke()
,调用函数的方式调用一个对象时的回应方法
<?php
class Person
{ public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
public function __invoke() {
echo '这可是一个对象哦';
}
}
$person = new Person('小明'); // 初始赋值
$person();
__set_state()
__set_state(),调用var_export()导出类时,此静态方法会被调用。
<?php
class Person
{
protected $sex;
public $name;
private $age;
public function __construct($name="", $age=25, $sex='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
public function t(){}
public static function __set_state($an_array){
$a = new self();
$a->name = $an_array['name'];
return $a;
}
}
$person = new Person('小明'); // 初始赋值
$person->name = '小红';
$a=var_export($person,true);
var_dump($a);
_debugInfo()
_debugInfo()
,打印所需调试信息、自动调用 PHP5.6以后出的魔术方法
<?php
class C {
private $prop;
public function __construct($val) {
$this->prop = $val;
}
public function __debugInfo() {
return [
'propSquared' => $this->prop ** 2,
];
}
}
//这里的 `**` 是乘方的意思,也是在PHP5.6.0及其以上才可以使用,详情请查看PHP手册
var_dump(new C(42));
类外部使用的方法
__autoload()
:实例化一个对象时,如果对应的类不存在,在该方法被调用。
类的自动加载
在系统开发过程中,不可能把所有的类都写在一个PHP文件中,当在一个PHP文件中需要调用另一个文件中声明的类时,就需要通过include或者require把这个文件引入。
在类文件众多的项目中,要将所有类的文件都逐个引入,是一个很让人头痛的工作。那么我们能不能再用到什么类的时候,在把这个类所在的PHP文件引入呢?
__autoload()
魔术方法实现
在PHP5中,可以定义个__autoload
函数,它会在试图使用尚未被定义的类时自动调用,通过调用此函数,脚本引擎在PHP出错失败前有了最后一个机会加载所需的类。
__autoload(
$className)函数接受的一个参数,就是你想加载类的类名,所以你做项目时,在组织定义类的文件名时,需要按照一定的规则,最好以类名为中心,也可以加上同意的前缀或后缀形成文件名。
set_include_path()
,是在脚本里动态地对PHP.ini
中include_path
进行修改的。动态设置包含目录(预定义include和require的路径范围)。
加载lib目录下的类
set_include_path(get_include_path().PATH_SEPAPATOR."lib/");
<?php
function __autoload($class){
var_dump($class);
include $class.".php";
}
//set_include_path(get_include_path().PATH_SEPARATOR."lib/");
$a=new a();
$a->say();
$b= new b();
$b->say();
其实这个__autuload
并不是最好的选择,用spl_autoload_register()
函数
spl_autoload_register()
spl_autoload_register()
此函数的功能就是把函数注册至spl的autoload函数栈中,并移除系统默认的__autolad()函数。
spl_autoload_register()可以调用多次。它实际上创建了autoload函数的队列,按定义时的顺序逐个执行。而__autoload()只可以定义一次
<?php
class test {
public static function loadprint( $class ) {
$file = $class . '.class.php';
if (is_file($file)) {
require_once($file);
}
}
}
spl_autoload_register( array('test','loadprint') );
//另一种写法:spl_autoload_register( "test::loadprint" );
$obj = new PRINTIT();
$obj->doPrint();?>
printit.class.php文件
<?php
class PRINTIT {
function doPrint() {
echo 'hello world2';
}
}
?>
spl_autoload_register()
多次调用示例
<?php
set_include_path(get_include_path().PATH_SEPARATOR."lib/");
function __autoload($class){
echo "__autoload".$class,"<br/>";
include $class.".php";
}
function __myautoload($class){
echo "__myautoload".$class,"<br/>";
include $class.".php";
}
function __toload($class){
echo "__toload中".$class,"<br/>";
}
spl_autoload_register("__myautoload");
spl_autoload_register("__toload");//这个函数是多次执行的
// 如果上一个函数没找到 会执行下一个spl的注册函数进行查找 会进入__toload
//以 “栈 ”的形式进行查找
//
$a=new a();
$a->say();
$b= new b();
$b->say();
$c= new c(); //new 一个没有的类 对象测试 spl_autoload_regiter的测试结果
命名空间引入类的原理
在当前文件夹在新建dong然后在里面新建个Text.php
<?php
namespace dongge;
use dong\Text;
use function var_dump;
class test {
public static function loadprint( $class ) {
$file = $class . '.php';
$file=str_replace("\\",'/',$file);
if(is_file($file)){
include $file;
}
}
}
//另一种写法: spl_autoload_register( array('test','loadprint') ,true,true);
spl_autoload_register( "dongge\\test::loadprint" );
$obj = new Text();
$obj->doPrint();
dong文件夹的Text.php
<?php
namespace dong;
class Text{
function doPrint() {
echo 'hello world2';
}
}
?>