学习教程来源于:
PHP中文网教程
PHP官网PHP手册(简体中文)链接
PHP面向对象编程(OOP)
OOP(面向对象编程)是一种编程思想,而不是技术
类与对象:
类是生成对象的模板
对象是类的一个实例
创建类
<?php
// 1.类名必须符合标识符定义
//推荐使用帕斯卡命名法,标识符的第一个首字母大写
class Demo
{
//属性和方法
//属性的声明必须以访问控制符开头
public $name = 'php'; //公共属性,外部可以访问
//属性访问控制符:public/private/peotected
private $age = 20; //私有属性,只允许类中的访问调用
protected $sex = 'male';//受保护的,仅允许本类或者子类访问
// 属性类型支持:标量(整数、浮点、字符串、布尔值),复合类型和对象
// 方法也必须以访问控制符开头:public/private/protected
public function getName()
{
//$this是伪变量,总是指向当前对象
return $this->name;
}
public function getAge()
{
return $this->age;
}
}
// 创建对象的过程就是类的实例化
$obj = new Demo(); // $obj 就是类Demo的实例
//类必须实例化才可以访问里面的属性和方法
echo $obj->name; //用对象访问符来直接访问属性
echo '<br/>';
echo $obj->getName();
// echo $obj->age; //private定义的属性不能在外部访问
echo '<br/>';
echo $obj->getAge();
//对象是一个引用变量,我们对对象的赋值并没有创建新的对象,而是创建一个对当前对象的引用
$obj2 = $obj; //并没有创建新对象
if ($obj2 = $obj;) {
echo '相等';
} else {
echo '不相等';
}
echo '<br/>';
echo get_class($obj2);
echo '<br/>';
echo get_class($obj);
echo $obj2->name;
echo $obj->name;
//如果在类的外部访问属性或方法,可以直接用对象
//如果在类的内部访问属性或方法,必须使用伪变量$this
创建对象的六种方法
<?php
class Demo1
{
public $name = 'php中文网';
public function getName()
{
return $this->name;
}
public function getObj()
{
return new self();
}
public function getStatic()
{
return new static();
}
}
//关键词 extends 继承
class Demo2 extends Demo1
{
public function getNewObj()
{
return new parent();
}
}
1.用new 类名()来创建一个对象
$obj = new Demo1();//如果不需要传入参数,可以省略括号
echo $obj->name;
echo '<hr/>';
2.将类名一字符串的方式放在一个变量中
$className = 'Demo1';
$obj1 = new $className();
echo $obj1->name;
echo '<hr/>';
3.用对象来创建对象,并且他创建的是一个新对象
$obj2 = new $obj(); //注意:$obj2 = $obj 不同
// echo get_class($obj2);
echo $obj2->name;
echo '<hr/>';
4.用new self()
$obj3 = $obj->getObj();
// echo get_class($obj);
echo $obj3->name;
echo '<hr/>';
5.用new parent() 来创建一个对象
$obj4 = (new Demo2)->getNewObj();
// echo get_class($obj4);
echo $obj4->name;
echo '<hr/>';
6.基于当前调用的类来创建:new static()
$obj5 = (new Demo1)->getStatic();
$obj6 = (new Demo1)->getObj();
echo get_class($obj5);
echo get_class($obj6);
echo '<hr/>';
$obj7 = (new Demo2)->getStatic();
$obj8 = (new Demo2)->getObj();
echo get_class($obj7); //new static()
echo get_class($obj8); //new self()
// new static()方式创建的对象直接与调用者绑定,静态延迟绑定
类常量
<?php
class Demo
{
// 类常量就是它的值在类中始终不变的量
// 类常量是用const关键字创建,不要加$,必须初始化
const siteName = 'php中文网';
// 类常量从php5.3开始支持nowdoc语法
const domain = <<< 'EOT'
<a href="">www.php.net</a>
//此处结束标签一定要在第一列
EOT;
public function getSiteName()
{
//在类的方法中访问类常量:self::内场了名
return self::siteName;
}
}
//方法1:类名::类常量名
echo '1.类名::类常量名:'.Demo::siteName.Demo::domain.'<hr/>';
//方法2:类变量::类常量名 php5.3+
$className = 'Demo';
echo '2.类变量::类常量名:'.$className::siteName.'<hr/>';
//方法3:用当前类的对象来访问类常量
echo '3.对象::类常量名:'.(new Demo)::siteName.'<hr/>';
//方法4:用类中的方法来间接访问类常量
echo '4.对象->方法():'.(new Demo)->getSiteName();
类的自动加载技术
<?php
//1.用require导入一个类文件
// require('test.php');//导入test.php
// include('test.php');
//2.__autoload()当我们引入一个不存在的类时,自动调用它导入该类文件
// 此方法php7.2+已弃用
// This feature has been DEPRECATED as of PHP 7.2.0. Relying on this feature is highly discouraged.
function __autoload($className)
{
$path = $className.'.php';//Test.php
if (file_exists($path)) {
require_once($path);
} else {
echo $path.'不存在,请检查~~';
}
}
3.自定义导入函数,用spl_autooad_register()将自定义的类导入函数添加到函数栈中
function loader($className)
{
$path = $className.'.php';//Test.php
if (file_exists($path)) {
require_once($path);
} else {
echo $path.'不存在,请检查~~';
}
}
spl_autoload_register('loader');
echo (new Test('我爱php'))->name;
将自定义的导入函数放到一个类的方法里
class LoaderClass
{
function loader($className)
{
$path = $className.'.php';//Test.php
if (file_exists($path)) {
require_once($path);
} else {
echo $path.'不存在,请检查~~';
}
}
}
// 当自定义的函数为类中的方法时,需要放入数组中
// spl_autoload_register(['类名/对象(调用者)','方法名']);
spl_autoload_register([(new LoaderClass),'loader']);
echo (new Test('欢迎来到php中文网学习~~'))->name;
// 静态方法
class LoaderClass
{
static function loader($className)
{
$path = $className.'.php';//Test.php
if (file_exists($path)) {
require_once($path);
} else {
echo $path.'不存在,请检查~~';
}
}
}
spl_autoload_register(['LoaderClass','loader']);
echo (new Test('欢迎来到php中文网~~'))->name;
类的构造方法与析构方法
<?php
// 构造方法:用来实例化类,创建对象的
class Staff
{
public $name;
public $age;
public $salary;
//构造方法使用固定的方法名:__construct()
public function __construct($name,$age,$salary)
{
// 构造方法:通常用来初始化对象中的属性
$this->name = $name;
$this->age = $age;
$this->salary = $salary;
}
// 析构方法对象销毁时调用,没有参数,__destruct()
public function __destruct()
{
echo '当前对象被销毁了~~';
}
}
// 创建一个对象,来访问类中的属性
$obj = new Staff('Peter', 28, 3500);
echo '姓名:'.$obj->name;
echo '<hr/>';
echo '年龄:'.$obj->age;
echo '<hr/>';
echo '薪水'.$obj->salary;
echo '<hr/>';
//销毁变量,使用unset()方法
unset($obj);
对象的封装
<?php
// 对象的封装,主要是指对象属性的封装,同private访问控制符
class Staff
{
private $name;
private $age;
private $salary;
public function __construct($name,$age,$salary)
{
$this->name = $name;
$this->age = $age;
$this->salary = $salary;
}
public function __get($name)
{
return $this->$name;
}
public function __set($name,$value)
{
if ($name == 'age') {
return false;
}
$this->$name = $value;
}
// __isset():检测是否存在某个属性
public function __isset($name)
{
return isset($this->$name);
}
// __unset(属性名):在类外部销毁某个私有属性时自动调用它
public function __unset($name)
{
unset($this->$name);
}
}
$obj = new Staff('Peter', 28, 4500);
echo $obj->name;
echo $obj->age;
echo $obj->salary;
echo '<hr/>';
$obj->name = 'Tom';
echo $obj->name;
echo '<hr/>';
$obj->age = 68;
echo $obj->age;
echo '<hr/>';
$obj->salary = 7900;
echo $obj->salary;
echo '<hr/>';
// 为什么上面的$obj->name可以检测到值,isset里面的就不行呢?
echo(isset($obj->name))?'存在':'不存在';
echo '<hr/>';
// unset($obj->age);
// echo $obj->age;
类的继承与多态
<?php
// 继承可以实现代码复用,构建类的等级关系
// 类Person,作为父类
class Person
{
protected $name;//protected受保护的,外部不可访问,只允许自己内部或子类访问
protected $age;
protected $salary;
// 创建构造方法,用来实例化这个类
public function __construct($name,$age,$salary)
{
$this->name = $name;
$this->age = $age;
$this->salary = $salary;
}
// 申明为收保护的,这样就只能被子类继承,子类继承过去仍然是protected
protected function showMess()
// public function showMess()
{
return '我的姓名是'.$this->name.',年龄是:'.$this->age.',工资是:'.$this->salary;
}
}
// 声明一个子类(扩展类),继承使用关键字:extends,php是单继承语言
// 创建子类是为了扩展父类的功能,实现代码复用
class Staff extends Person
{
protected $department;//员工所在的部门
public function __construct($name,$age,$salary,$department)
{
// 继承父类属性
parent::__construct($name,$age,$salary);
$this->department = $department;
}
// 在子类中重写父类方法时,其访问权限不能低于原来的,原来是protected,那么现在就应该是public(访问权限:public>protected>pravited)
public function showMess()
{
return parent::showMess().',部门是:'.$this->department;
}
}
$obj = new Staff('小明',23,1500,'开发部');
echo $obj->showMess();
// 多态,全称为多种状态,与方法重载非常类似,可以根据传入参数不同,提供不同的服务, php不支持多继承,不过,php5.4+提供了trait技术,类似于在当前类中插入一个外部方法集,实现了部分多继承功能
// 多态是通过对父类中的方法进行重写实现,在子类中定义一个与父类同名的方法就会对其进行重写
类中静态成员的创建与访问技巧
<?php
class Father
{
//访问控制符:指示类成员在哪里可以被访问:public/private/protected
//成员状态符:指示如何访问该成员:静态self/parent/static 非静态:$this->
public static $name = 'peter'; //公共静态属性,类内部/外部/子类均可访问
private static $age = 28; //私有静态属性,只能在类内部访问
protected static $salary = 3600;//受保护的静态属性,可以在类内部和子类中访问
public static function show () //静态方法
{
//非静态方法可以调用静态方法,静态方法不可以调用非静态方法!
return '年龄:'.self::$age.'---'.'工资'.self::$salary;
}
}
// 创建子类Son,继承自Father类
class Son extends Father
{
public static function display()
{
//parent::引用父类中的静态成员
return '工资是:'.parent::$salary;
}
}
echo '姓名是:'.Father::$name;//外部访问静态成员,使用类名::静态成员,静态属性必须加$符号
//静态成员:静态属性和静态方法
echo '<hr/>';
echo Father::show();//访问类中的静态方法
echo '<hr/>';
echo Son::show(); //用子类访问父类中的静态方法
echo '<hr/>';
echo Son::display();//访问子类的静态方法
echo '<hr/>';
$obj = new Father;
echo $obj->show(); //外部使用对象,也可以访问静态方法
// echo $obj->name; //外部对象不能访问类中的静态属性
echo '<hr/>';
$res = $obj instanceof Father;
echo '$obj是Father类的实例吗?'.($res?'是的':'不是的');
类的静态绑定与延迟绑定技术
<?php
class Demo1
{
public static $name = 'peter';
public static $salary = 3000;
public static function show()
{ //self::与Demo1类静态绑定
// return self::$name;//访问本类中的静态属性self::就是当前类
// return static::$name;//访问本类中的静态属性static::就是当前类
return static::$sex;//static 与self,parent是不一样的,它对应的类是动态设置的,由调用类决定,如果说self和parent是静态绑定的话,static就是动态绑定到类,叫做静态延迟绑定(后期静态绑定)
// 静态绑定(self和parent)它们与类的绑定在代码的编译阶段进行,
//而static与类的绑定实在代码的运行时才进行绑定,所以叫做:静态延迟绑定(与类的绑定机制不同)
//static 谁调用它,就和谁绑定
}
}
class Demo2 extends Demo1
{
public static $sex = 'male';
public static function display()
{
//parent::与父类进行绑定,self::与Demo2类静态绑定
// return parent::$name.'的工资是:'.parent::$salary.' 性别是:'.self::$sex;
//parent::与父类进行绑定,static::与Demo2类静态绑定
return parent::$name.'的工资是:'.parent::$salary.' 性别是:'.static::$sex;
}
}
// echo '姓名是:'.Demo1::$name;//在外部访问类中的静态属性
// echo '<hr/>';
// echo '姓名是:'.Demo1::show();//在外部访问类中的静态方法
// echo '<hr/>';
// echo Demo2::display();
echo '性别是:'.Demo2::show();
对象的克隆技术
<?php
class Demo
{
public $name = 'peter';
}
$obj1 = new Demo;
$obj2 = $obj1; //对象都是引用赋值
$obj3 = clone $obj1; //克隆,相当于值传递赋值,将当前对象复制到新的变量中
$obj4 = new Demo;
$obj1->name = 'Jack';//重新设置对象$obj1中的属性name的值
echo '对象引用:'.$obj1->name,'---',$obj2->name;
echo '<hr/>';
echo '克隆赋值:'.$obj1->name,'---'.$obj3->name;
echo '<hr/>';
echo '创建对象:'.$obj1->name,'---'.$obj4->name;
echo '<hr/>';
echo '克隆对象的类是:'.get_class($obj3);
// 克隆就是将当前对象复制一份镜像,与重新new一个对象完全相同
// 对象赋值是引用,仅仅是给当前对象起一个别名,并没有创建新对象
// 关键字clone克隆出的一个与原来对象毫无关系的新对象
访问不存在的方法或不存在的静态方法
<?php
class Demo
{
//第一个参数是方法名,第二个参数是方法参数,以数组形式传入
public function __call($method,$argus)
{
// 遍历参数argus
$var = '';
foreach ($argus as $value) {
$var .= $value.',';//.= 字符串连接运算
}
return '方法是'.$method.'('.$var.')'.'不存在';
}
//当我们调用一个不存在的静态方法时,会自动调用__callStatic()
public static function __callStatic($method,$argus)
{
// 遍历参数argus
$var = '';
foreach ($argus as $value) {
$var .= $value.',';//.= 字符串连接运算
}
return '静态方法是'.$method.'('.$var.')'.'不存在';
}
}
// 当访问一个不存在的静态方法是,自动调用类中的魔术方法:__call()
echo (new Demo)->hello('php','python');
echo '<hr/>';
// 当访问一个不存在的静态方法是,自动调用类中的魔术方法:__callStatic()
echo Demo::hello(10,20,30);
对象的序列化
<?php
class Staff
{
public $name;
public $age;
public $salary;
public function __construct($name,$age,$salary=0)
{
$this->name = $name;
$this->age = $age;
$this->salary = $salary;
}
public function __sleep()
{
//将允许序列化的对象属性放在一个数组中返回
return ['name','age'];
}
public function __wakeup()
{
$this->age = 48;
}
}
$obj1 = new Staff('peter',28,5000);
// 序列化之前对象属性
echo '我的姓名是:'.$obj1->name,'年龄是:'.$obj1->age,'工资是:'.$obj1->salary;
echo '<hr/>';
$objStr = serialize($obj1);
echo '序列化的对象:'.$objStr;
echo '<hr/>';
// 反序列化
$obj2 = unserialize($objStr);
echo '我的姓名是:'.$obj2->name,'年龄是:'.$obj2->age;
抽象方法与抽象类
<?php
// 抽象类不能单独使用,不能被实例化,只能通过子类进行调用。可以看做子类的一个模板。
// 当一个类中有有抽象方法时,它就需要被声明为抽象类
abstract class Demo
{
public $name;
public function __construct($name)
{
$this->name = $name;
}
abstract public function hello();
abstract public function say();
public function test()
{
return 'Demo::test()';
}
}
// 抽象方法必须被一个子类继承,才能使用
class Demo1 extends Demo
{
// 必须在子类中加工抽象类中的全部抽象方法实现才可以
public function hello()
{
return 'hello'.$this->name;
}
public function say()
{
return '我的name是:'.$this->name;
}
}
$obj = new Demo1('php');
echo $obj->hello();
echo '<hr/>';
echo $obj->say();
echo '<hr/>';
echo $obj->test();
// 抽象类只是规范了子类要有父类中声明的抽象方法,具体的实现是由子类决定的
接口技术的实现
<?php
// 接口的访问控制符必须是public
// 接口1:Demo1
interface Demo1
{
// 接口成员属性必须是:类常量
const SITENAME = 'PHP中文网';
// 接口成员的方法必须是抽象方法,而且访问控制符必须是public,abstract也可以省略
function show();
function mess();
}
// 接口2:Demo2
interface Demo2
{
function hello();
}
// 接口不允许实例化,但可以被继承,所以需要创建一个类,来继承接口,并实现接口中全部抽象方法
class Test implements Demo1,Demo2
{
// 实现接口Demo1中的show()方法
public function show()
{
return '站点名称是:'.self::SITENAME;
}
// 实现接口Demo1中的mess()方法
public function mess()
{
return '站点域名是:www.php.cn';
}
// 实现接口Demo2中的hello()方法
public function hello()
{
return self::SITENAME.'欢迎您~~';
}
}
$obj = new Test;
echo $obj->show();//访问接口Demo1中的show()方法
echo '<hr/>';
echo $obj->mess();//访问接口Demo1中的mess()方法
echo '<hr/>';
echo $obj->hello();//访问接口Demo2中的hello()方法
echo '<hr/>';
trait特性的声明和使用技巧
创建一个trait类Test1
trait Test1
{
public $name = 'PHP中文网';//trait类中可以有属性,不能有类常量
public function hello1()
{
return 'Test::hello1()';
}
}
// 2.创建一个trait内Test2
trait Test2
{
use Test1;
public function hello2()
{
return 'Test2::hello2()'.$this->name;
}
}
// 3.创建父类
class Demo
{
public function hello2()
{
return '父类Demo::hello2()'.$this->name;
}
}
// 4.创建类Demo1
class Demo1 extends Demo
{
use Test2;
// 同一类中,同名方法的访问优先级:子类>trait类>父类
public function hello2()
{
return '子类Demo1::hello2()'.$this->name;
}
}
// 测试
$obj = new Demo1;
echo $obj->hello1();//访问trait类Test1中的hello1()
echo '<hr/>';
echo $obj->name; //访问呢trait类Test1中的属性
echo '<hr/>';
echo $obj->hello2();
trait Test1
{
public function hello()
{
return 'Test1::hello()';
}
}
trait Test2
{
public function hello()
{
return 'Test2::hello()';
}
}
class Demo
{
use Test1,Test2{
// 用Test1:hello()替代Test2::hello()
Test1::hello insteadof Test2;
// 用别名访问Test2::hello()方法
Test2::hello as test2Hello;
}
}
// 测试
$obj = new Demo;
echo $obj->hello();
echo '<hr/>';
echo $obj->test2Hello();//test2Hello 是Test2::hello()方法的别名
命名空间
<?php
namespace test1;
const SITE_NAME = 'php中文网';//声明常量SITE_NAME
function sum($n, $m)//声明函数sum()
{
return $n+$m;
}
class Staff
{
private $name = 'peter';
public function __get($name)
{
return $this->$name;
}
public function __set($name,$value)
{
return $this->$name = $value;
}
}
namespace test2;
// use test1; //只定位到空间 并不定位到类
use test1\Staff as test1Staff; //定义到空间中的类
use test2\test3\Demo;
const SITE_NAME = 'www.php.cn';//声明常量SITE_NAME
function sum($n, $m)//声明函数sum()
{
return $n+$m;
}
class Staff
{
private $name = 'jack';
public function __get($name)
{
return $this->$name;
}
public function __set($name,$value)
{
return $this->$name = $value;
}
}
//访问
echo '当前的命名空间:'.__NAMESPACE__;
echo '<hr/>';
echo SITE_NAME;//非限定名称的命名空间
echo '<hr/>';
echo \test1\SITE_NAME;//完全限定名称的命名空间
echo '<hr/>';
echo sum(10, 20);
echo '<hr/>';
$obj = new Staff;
echo $obj->name;
echo '<hr/>';
$obj1 = new Staff;
$obj1->name = 'Tom';
echo $obj1->name;
echo '<hr/>';
echo Demo::CITY;
/*
代码的命名空间分为俩类:
1.全局空间(公共空间):其中的类、常量、函数都是全局成员,不允许重名
2.可命名空间:可以重名,不冲突
实际开发中一个文件用一个命名空间
*/
namespace test2\test3;
class Demo
{
const CITY = '合肥';
}
对象的遍历
<?php
class Demo
{
public $name;
public $age;
public $salary;
private $sex;
protected $isMarried;
//静态属性不属于对象,属于类
public static $home;
// 声明构造方法,用来初始化对象
public function __construct($name,$age,$salary,$sex,$isMarried,$home)
{
$this->name = $name;
$this->age = $age;
$this->salary = $salary;
$this->sex = $sex;
$this->isMarried = $isMarried;
self::$home = $home;
}
// 声明一个query方法,用来在类的内部遍历属性
public function query()
{
print '遍历出对象的全部属性,包括私有和受保护的:<br/>';
foreach ($this as $key => $value){
echo $key.'=>'.$value.'<br/>';
}
print self::$home;
}
}
// 外部访问
$obj = new Demo('peter', 28, 3800, 'male', true, '合肥');
// 遍历对象
echo '外部访问的公共属性:<br/>';
foreach ($obj as $key => $value){
echo $key.'=>'.$value.'<br/>';
}
echo Demo::$home;//外部使用类名访问静态成员;
echo '<hr/>';
$obj->query();//遍历出对象中的全部属性
PHP5.6对命名空间的扩展
use可以导入函数可以常量空间
此处代码暂时有问题,暂不讨论。
详细相关请参考:PHP手册 使用命名空间:别名/导入