今天,我们学习了静态成员与方法重载,具体内容由对象成员的访问限制,静态属性 方法的定义与访问,类常量与静态属性的区别与相同,静态属性方法的重载,还做了案例:仿Thinkphp的连式调用。
一、对象成员的访问限制
访问限定符规定了对象属性、方法的作用域,public在类内类外子类都可用,protected在类内 子类可用 ,private在类内可用,但是要在类外访问protected private的属性怎么办,通常是先用构造函数把各个属性初始化,然后在通过public的返回,外部就可以调用了。
实例
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.'
';
//echo $obj->position.'
';
//echo $obj->salary.'
';
echo $obj->getPosition().'
';
echo $obj->getSalary().'
';
$obj->setSalary(1111);
echo $obj->getSalary().'
';
/**************************************/
class sub extends Demo06
{
}
$obj = new sub('豆豆', '职员', 8888, '财务部');
echo $obj->getSalary().'
';
echo $obj->getPosition();
运行实例 »
点击 "运行实例" 按钮查看在线实例
二、静态成员的定义与访问
类一般作为实例化对象的模版,但是存在以下的情况,可以不实例化成对象,直接用类来访问属性 方法,这就是静态属性、静态方法,统称为静态成员。
情况一、有时一个类只实例化成一个对象,那么为何不用类直接访问属性和方法?
情况二、有时同一个类实例化的许多对象,都有共同的属性需要调用,这样为何不给类一个统一属性,让他们共同调用,而不是实例化一次调用一次
具体访问是是不需要实例化 直接类::属性名 或者类::方法()
ps实例化的对象不能访问静态属性 但是可以访问静态的方法
实例
//类一般作为实例化对象的模版,访问类中的属性 方法都是先实例化成对象然后调用,但是可不可以通过类直接访问?
//静态属性 静态方法可以不实例化访问,静态成员的引入可以解决两个问题
//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.'
';
echo Demo01::$price.'
';
echo $obj->getInfo1().'
';
echo Demo01::getInfo2($obj->product).'
';
//对象不能访问静态属性 但是可以访问静态方法
echo $obj->getInfo2($obj->product);
运行实例 »
点击 "运行实例" 按钮查看在线实例
三、类常量的定于与访问
类常量与静态属性基本一致,不同的是类常量的值不允许改变,静态属性可以被修改
实例
//类常量
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.'
';
}
}
$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)是销毁属性
实例
//属性重载
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.'
';
$obj = new Demo11('admin', 9999);
echo $obj->secret.'
';
$obj->salary = 8888;
echo $obj->salary;
echo "
";
isset($obj->salary);
echo '
';
unset($obj->salary);
isset($obj->salary);
运行实例 »
点击 "运行实例" 按钮查看在线实例
五、方法重载
针对在类外不能访问的方法由__call 今天方法__callStatic()
还有两个方法 call_user_func() call_user_func_array() 他们将函数名作为参数,函数里的参数作为参数的两个方法,后者是吧参数用数组的形式穿进去
实例
//方法重载
class Demo12
{
//出现没有的类方法是自动调用__call $name是方法名,$arguments 是所有参数
public function __call($name, $arguments)
{
return '方法名是 '. $name . '
参数列表是' .'
'.print_r($arguments, true);
}
//也适用于静态方法
public static function __callStatic($name, $arguments)
{
return '方法名是 '. $name . '
参数列表是' .'
'.print_r($arguments, true);
}
}
$obj = new Demo12();
echo $obj->getInfo('jj', 'kk', 123);
echo '
';
echo Demo12::getInfo11(10, 20, 30);
echo '
';
// 做个案例
//先了解两个函数
//call_user_func() 就是将一个函数作为参数 这个函数的参数做为他的参数 进行回调执行
//call_user_func_array() 和上面基本一致 只不过第二个参数是回调函数的参数组成的数组
function sum ($a, $b) {
return $a . '+' . $b .'='.($a+$b);
}
echo sum(10, 20);
echo '
';
//回调的方式为
echo call_user_func('sum', 10, 20);
echo '
';
echo call_user_func_array('sum', [10, 20]);
echo '
';
//那么把方法放在函数里 也可以调用
class Test1
{
public function sum ($a, $b) {
return $a . '+' . $b .'='.($a+$b);
}
}
$obj = new Test1();
echo call_user_func([$obj, 'sum'], 20, 40);
echo '
';
echo call_user_func([new Test1(), 'sum'], 20, 40);
echo '
';
//类的静态方法也适用
class Test2
{
public static function mul ($a, $b) {
return $a . '*' . $b .'='.($a*$b);
}
}
echo call_user_func('Test2::mul', 20, 40);
echo '
';
//也可以
echo call_user_func_array(['Test2', 'mul'], [20, 40]);
echo '
';
echo Test2::class; //可以输出类的字符串
echo '
';
//也可以
echo call_user_func_array([Test2::class, 'mul'], [20, 40]);
运行实例 »
点击 "运行实例" 按钮查看在线实例
六、案例:连式调用
实例
//模拟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 '
'.print_r($staff, true);
}
运行实例 »
点击 "运行实例" 按钮查看在线实例
Query.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 '
'.print_r($staff, true);
}
运行实例 »
点击 "运行实例" 按钮查看在线实例
总结
1、静态成员可以不用实例化不需要对象调用,有时比较简便
2、类常量是不能修改的
3、属性重载可以不用每个访问不到的属性单独设置获取的方法,只需要一个获取去 根据属性名来获取属性值
4、方法 重载同属性重载的意义一样,这里需要注意call_user_func call_user_func_array 这俩个回调函数的妙用,在后面的案例中,他作为跨类访问的入口,可以现在另外的类中定义好各种方法 然后根据情况在调用
5、连式调用的核心在与每个方法最后都返回$this 对象本身,这样才能让下一个方法由对象看调用 实现连式调用