今天,我们学习了静态成员与方法重载,具体内容由对象成员的访问限制,静态属性 方法的定义与访问,类常量与静态属性的区别与相同,静态属性方法的重载,还做了案例:仿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 对象本身,这样才能让下一个方法由对象看调用 实现连式调用