今天,我们学习了静态成员与方法重载,具体内容由对象成员的访问限制,静态属性 方法的定义与访问,类常量与静态属性的区别与相同,静态属性方法的重载,还做了案例:仿Thinkphp的连式调用。

一、对象成员的访问限制

访问限定符规定了对象属性、方法的作用域,public在类内类外子类都可用,protected在类内 子类可用 ,private在类内可用,但是要在类外访问protected private的属性怎么办,通常是先用构造函数把各个属性初始化,然后在通过public的返回,外部就可以调用了。

实例

<?php
class Demo06
{
    public $name;           //姓名
    protected $position;    //职位
    private $salary;        //工资
    protected $department;  //部门
    //构造函数
    public function __construct($name, $position, $salary, $department)
    {
        $this->name = $name;
        $this->position = $position;
        $this->salary = $salary;
        $this->department = $department;
    }
//    获取职位的方法
    public function getPosition(){
        return $this->department === '培训部' ? $this->position : '无权查看';
    }
//    获取工资的方法
    public function getSalary(){
        return $this->department ==='财务部' ? $this->salary : '无权查看';
    }
    public function setSalary($salary){
        return $this->department ==='财务部' ? $this->salary = $salary : '无权更新';
    }
}

$obj = new Demo06('明仔', '程序员', 9999, '培训部');
//$obj = new Demo06('明仔', '程序员', 9999, '财务部');
echo $obj->name.'<br>';
//echo $obj->position.'<br>';
//echo $obj->salary.'<br>';
echo $obj->getPosition().'<br>';
echo $obj->getSalary().'<br>';
$obj->setSalary(1111);
echo $obj->getSalary().'<hr>';
/**************************************/
class sub extends Demo06
{

}
$obj = new sub('豆豆', '职员', 8888, '财务部');
echo $obj->getSalary().'<br>';
echo $obj->getPosition();

运行实例 »

点击 "运行实例" 按钮查看在线实例

二、静态成员的定义与访问

类一般作为实例化对象的模版,但是存在以下的情况,可以不实例化成对象,直接用类来访问属性 方法,这就是静态属性、静态方法,统称为静态成员。

情况一、有时一个类只实例化成一个对象,那么为何不用类直接访问属性和方法?

秒速快三情况二、有时同一个类实例化的许多对象,都有共同的属性需要调用,这样为何不给类一个统一属性,让他们共同调用,而不是实例化一次调用一次

具体访问是是不需要实例化  直接类::属性名  或者类::方法()

ps实例化的对象不能访问静态属性 但是可以访问静态的方法

实例

<?php
//类一般作为实例化对象的模版,访问类中的属性 方法都是先实例化成对象然后调用,但是可不可以通过类直接访问?
//静态属性 静态方法可以不实例化访问,静态成员的引入可以解决两个问题
//1就是有的时候只需要一个对象  那么实例化和直接用类是一样的
//2有的时候实例化成许多的对象,如果需要共享的属性,还的一个个赋值给属性  麻烦

class Demo01
{
    public $product;
    public static $price;
//    构造函数舒适化
    public function __construct($product, $price)
    {
        $this->product = $product;
        self::$price = $price;
    }
//    对象方法
    public function getInfo1(){
        return $this->product.'价格是:  '.self::$price;
    }
//    静态方法
    public static function getInfo2($product){
        return $product."价格是:   ".self::$price;
    }
}
$obj = new Demo01('***', 5433);
echo $obj->product.'<br>';
echo Demo01::$price.'<br>';
echo $obj->getInfo1().'<br>';
echo Demo01::getInfo2($obj->product).'<br>';
//对象不能访问静态属性  但是可以访问静态方法
echo $obj->getInfo2($obj->product);

运行实例 »

点击 "运行实例" 按钮查看在线实例

三、类常量的定于与访问

类常量与静态属性基本一致,不同的是类常量的值不允许改变,静态属性可以被修改

实例

<?php
//类常量
class Demo02
{
    //类常量同类属性一样,不需要实例化调用,而是直接由类调用
    //区别是类常量不能改变,类属性可以改变
    const NATION = '中国';
    public static $sex = '男';
    private $name;
    public function __construct($name)
    {
        $this->name = $name;
    }
    public function getInfo()
    {
        //类常量和类属性一样在类内调用的话用self
        return $this->name.'性别是: '.self::$sex.',国籍是:'.self::NATION.'<br>';
    }
}
$obj = new Demo02('明仔');
echo $obj->getInfo();
//修改类属性
Demo02::$sex = '保密';
//修改类常量出错
//Demo02::NATION = 'riben';
echo $obj->getInfo();

运行实例 »

点击 "运行实例" 按钮查看在线实例

四、属性重载

但在类外访问protected  private属性是统称被限制,针对上述情况,由魔术方法来解决问题。

__get($name)  出现不能访问的情况,就自动调用此方法,然后将属性名作为参数穿进去,然后该方法在进行处理

__set($name, $value) 这方法是设置器

秒速快三__isset($name) 这是判断是否由该属性

__unset($name)是销毁属性

实例

<?php
//属性重载
class Demo11
{
    //设置私有属性
    private $name;
    private $salary;
    protected $secret = '我有一个小秘密';
    public function __construct($name, $salary)
    {
        $this->name = $name;
        $this->salary = $salary;
    }
    //用__get 出现不能显示的类属性自动调用该方法作为获取器
    public function __get($name)
    {
        //对用户进行判断 如果要查看secret 那么必须是admin的用户才能查看 否则无权查看
        //  其他的类属性就可以直接查看
        if ($name === 'secret') {
            return $this->name === 'admin' ? $this->$name : '无权查看';
        }
        return $this->$name;
    }
    //__set 出现不能显示的类属性后 对该属性进行设置

    public function __set($name, $value)
    {
        // 直接返回, 极少这样做
//        $this->$name = $value;

        // 添加过滤机制
        if ($name === 'salary') {
            return $this->name === 'admin' ? $this->$name = $value : '无权修改';
        }
        return $this->$name = $value;
    }

    public function __isset($name)
    {
        if ($this->name === 'admin') {
            if (isset($this->$name)) {
                echo '属性存在';
            } else{
                echo '属性不存在';
            }
        } else{
            echo '无权检测';
        }
    }

    public function __unset($name)
    {
        if ($this->name === 'admin') {
            unset($this->$name);
        } else {
                echo '无权删除';
        }
    }

}

$obj = new Demo11('明仔', 1111);

echo $obj->name.'<br>';
$obj = new Demo11('admin', 9999);
echo $obj->secret.'<br>';
$obj->salary = 8888;
echo $obj->salary;
echo "<hr>";
isset($obj->salary);
echo '<br>';
unset($obj->salary);
isset($obj->salary);

运行实例 »

点击 "运行实例" 按钮查看在线实例

五、方法重载

针对在类外不能访问的方法由__call  今天方法__callStatic()

还有两个方法  call_user_func()  call_user_func_array()  他们将函数名作为参数,函数里的参数作为参数的两个方法,后者是吧参数用数组的形式穿进去

实例

<?php
//方法重载
class Demo12
{
    //出现没有的类方法是自动调用__call  $name是方法名,$arguments 是所有参数
    public function __call($name, $arguments)
    {
        return '方法名是  '. $name . '<br> 参数列表是' .'<pre>'.print_r($arguments, true);
    }
    //也适用于静态方法
    public static function __callStatic($name, $arguments)
    {
        return '方法名是  '. $name . '<br> 参数列表是' .'<pre>'.print_r($arguments, true);
    }

}
$obj = new Demo12();
echo $obj->getInfo('jj', 'kk', 123);
echo '<hr>';
echo Demo12::getInfo11(10, 20, 30);
echo '<hr>';

//  做个案例
//先了解两个函数
//call_user_func()  就是将一个函数作为参数  这个函数的参数做为他的参数 进行回调执行
//call_user_func_array()  和上面基本一致  只不过第二个参数是回调函数的参数组成的数组
function sum ($a, $b) {
    return $a . '+' . $b .'='.($a+$b);
}

echo sum(10, 20);
echo '<br>';
//回调的方式为
echo call_user_func('sum', 10, 20);
echo '<br>';
echo call_user_func_array('sum', [10, 20]);
echo '<br>';
//那么把方法放在函数里 也可以调用
class Test1
{
     public function sum ($a, $b) {
         return $a . '+' . $b .'='.($a+$b);
     }
}
$obj = new Test1();

echo call_user_func([$obj, 'sum'], 20, 40);
echo '<br>';
echo call_user_func([new Test1(), 'sum'], 20, 40);
echo '<br>';

//类的静态方法也适用
class Test2
{
    public static function mul ($a, $b) {
        return $a . '*' . $b .'='.($a*$b);
    }
}

echo call_user_func('Test2::mul', 20, 40);
echo '<br>';
//也可以
echo call_user_func_array(['Test2', 'mul'], [20, 40]);
echo '<br>';
echo Test2::class;    //可以输出类的字符串
echo '<br>';
//也可以
echo call_user_func_array([Test2::class, 'mul'], [20, 40]);

运行实例 »

点击 "运行实例" 按钮查看在线实例

六、案例:连式调用

实例

<?php
//模拟ThinkPhp 连式操作 查询数据库
//引用外部类
require 'Query1.php';
class Db1
{
    //数据库连接对象初始化
    protected static $pdo = null;
    //数据库连接方法 实现惰性连接 每次用时在连接  节省资源
    public static function connection () {
        self::$pdo = new PDO('mysql:dbname=php', 'root', 'root');
    }
    //  查询类的入口  通过用__callStatic  方法实现跨类调用
    public static function __callStatic($name, $arguments)
    {
        //创建pdo对象连接数据库
        self::connection();
        //实例化查询类 将连接对象作为参数
        $query = new Query1(self::$pdo);
        return call_user_func_array([$query, $name], [$arguments[0]]);
    }
}


$staffs = Db1::table('staff')
    ->field('id,name,position,mobile')
    ->where('id > 5')
    ->limit(5)
    ->select();
foreach ($staffs as $staff) {
    echo '<pre>'.print_r($staff, true);
}

运行实例 »

点击 "运行实例" 按钮查看在线实例

Query.php

实例

<?php
//模拟ThinkPhp 连式操作 查询数据库
//引用外部类
require 'Query1.php';
class Db1
{
    //数据库连接对象初始化
    protected static $pdo = null;
    //数据库连接方法 实现惰性连接 每次用时在连接  节省资源
    public static function connection () {
        self::$pdo = new PDO('mysql:dbname=php', 'root', 'root');
    }
    //  查询类的入口  通过用__callStatic  方法实现跨类调用
    public static function __callStatic($name, $arguments)
    {
        //创建pdo对象连接数据库
        self::connection();
        //实例化查询类 将连接对象作为参数
        $query = new Query1(self::$pdo);
        return call_user_func_array([$query, $name], [$arguments[0]]);
    }
}


$staffs = Db1::table('staff')
    ->field('id,name,position,mobile')
    ->where('id > 5')
    ->limit(5)
    ->select();
foreach ($staffs as $staff) {
    echo '<pre>'.print_r($staff, true);
}

运行实例 »

点击 "运行实例" 按钮查看在线实例

总结

1、静态成员可以不用实例化不需要对象调用,有时比较简便

2、类常量是不能修改的

3、属性重载可以不用每个访问不到的属性单独设置获取的方法,只需要一个获取去 根据属性名来获取属性值

4、方法 重载同属性重载的意义一样,这里需要注意call_user_func call_user_func_array  这俩个回调函数的妙用,在后面的案例中,他作为跨类访问的入口,可以现在另外的类中定义好各种方法 然后根据情况在调用

5、连式调用的核心在与每个方法最后都返回$this 对象本身,这样才能让下一个方法由对象看调用  实现连式调用