面向对象思想的核心概念

PHP的魔术方法

__construct方法:PHP中没有构造方法的重载

__destruct方法:析构方法允许在销毁一个对象之前执行一些特定操作,例如关闭文件、释放结果集等

当堆内存段中的对象失去访问它的引用时,就不能被访问了,也就成为垃圾对象了。通常对象的引用被赋予其他的值或者是在页面运行结束时,对象都会失去引用

访问属性:使用下面的方法之后,访问不存在的属性时不会报错

__set方法:void __set(string name, mixed value)  第一个参数需要传入在为私有属性设置值时的属性名,第二个参数则需要传入为属性设置的值。这个方法不需要主动调用,可以在方法前面也加上private修饰,防止用户直接调用。这个方法是在用户值为私有属性设置值时自动调用的

__get方法:这两个方法是用来完成对所有私有属性都能获取和赋值的操作

__isset方法:用来检查私有属性是否存在的方法

__unset方法:用来删除对象中私有属性的方法

php
class Account {
    private $user = 1;
    private $pwd = 2;

    public function __set($name, $value) {
        echo "Setting $name to $value \r\n";
        echo "
"; $this -> $name = $value; } public function __get($name) { if (!isset($this->$name)) { echo "未设置"; echo "
"; $this -> $name = "正在设置默认值"; } return $this->$name; } } $a = new Account(); echo $a -> user; echo "
"; $a -> name = 5; echo $a -> name; echo "
"; echo $a -> big; ?> //1 Setting name to 5 5 未设置 Setting big to 正在设置默认值 正在设置默认值

访问方法:使用下面的方法之后,访问不存在的方法不会报错

__call方法

mixed __call(string $name, array $arguments)
//当调用一个不可访问的方法(未定义,或者不可见)时,__call()会被调用。其中$name参数是要调用的方法名称
//$arguments参数是一个数组,包含着要传递给方法的参数

//使方法的动态创建成为可能,在MVC等框架设计中是很有用的语法。假设一个控制器调用了不存在的方法,那么只要定义了__call魔术方法,就能友好地处理这种情况
php
class TestClass {
    function printHello(){
        echo "Hello
"; } function __call($functionName, $args) { // TODO: Implement __call() method. echo "你所调用的函数: ".$functionName."(参数: "; print_r($args); echo ")不存在
\n"; } } $obj = new TestClass(); $obj->myFun("one", 2, "three"); $obj->otherFun(8, 9); $obj->printHello(); ?>

 

__callStatic方法

 

__toString方法:是在直接输出对象引用时自动调用的方法

如果类定义了__toString方法,就能在测试时,echo打印对象体,对象就会自动调用它所属类定义的__toString方法,格式化输出这个对象所包含的数据

仍然可以用print_r和var_dump函数输出一个对象

直接echo一个对象就会报语法错误,而如果这个对象实现__toString方法后就可以直接输出==>echo本来可以打印一个对象,而且也实现了这个接口,但是PHP对其做了个限制,只有实现__toString后才允许使用

php
//声明一个测试类,在类中声明一个成员属性和一个__toString()方法

class TestClass {
    private $foo;

    function __construct($foo)
    {
        $this->foo = $foo;
    }

    public function __toString()
    {
        // TODO: Implement __toString() method.
        return $this->foo;
    }
}

$obj = new TestClass('Hello');
echo $obj;
?>

 

对象串行化

串行化  serialize()函数  参数为对象的引用名,返回值为一个对象被串行化后的字符串

反串行化  unserialize()函数  把对象串行化后转换的二进制字符串再转换为对象  参数为serialize()函数的返回值,返回值是重新组织好的对象

 

类的组合与继承

组合是指在一个类中创建另一个类的对象,并把后者作为前者的一个属性,并调用它的方法处理问题,这种复用方式叫“组合”

::操作符用于调用父类的方法,还用来作为类常量和静态方法的调用

低耦合指模块与模块之间,尽可能地使模块间独立存在;模块与模块之间的接口尽量少而简单

继承:

子类重写父类的方法:可以使用parent::方法名来获取父类的代码

 

常见关键字:

final

static  类名::静态属性名  类名::静态成员方法名

在类中声明的成员方法中,也可以使用self来访问其他静态成员,因为静态成员是属于类的,所以不能通过$this访问

self::静态成员属性名

self::静态成员方法名

如果在类的外部访问静态成员,可以使用类名或者对象名来引用

 

单态设计模式

php
/**
声明一个类Db,用于演示单态模式的使用
 */
class DB {
    //声明一个私有的、静态的成员属性$obj
    private static $obj = null;  
    
    //构造方法,使用private封装后则只能在类的内部使用new去创建对象
    private function __construct()
    {
        //完成数据库连接操作
        echo "连接数据库成功
"; } //只有通过这个方法才能返回本类的对象 static function getInstance(){ if(is_null(self::$obj)) { self::$obj = new self(); } return self::$obj; } //执行SQL语句完成对数据库的操作 function query($sql) { echo $sql; } } $db = DB::getInstance(); $db->query("select * from user"); ?>

 

const关键字:

在PHP中定义常量是通过调用define()函数来完成的,但要将类中的成员属性定义为常量,则只能使用const关键字。将类中的成员属性使用const关键字标识为常量,其访问的方式和静态成员一样,都是通过类名或者在成员方法中使用self关键字访问,也不能用对象来访问

使用const声明的常量名称前不使用$

instanceof关键字

 

克隆对象:克隆以后,原本和副本两个对象完全独立、互不干扰

php
class Person {
    private $name;
    private $sex;
    private $age;
    
    function __construct($name = "", $sex = "",$age=1)
    {
        $this->name = $name;
        $this->sex = $sex;
        $this->age = $age;
    }
    
    function say(){
        echo "我的名字: ".$this->name.", 性别: ".$this->sex.", 年龄: ".$this->age."
"; } } $p1 = new Person("张三", "男", 20); $p2 = clone $p1; $p1 -> say(); $p2 -> say(); ?> php //如果需要对克隆后的副本对象在克隆时重新为成员属性赋初值,则可以在类中声明一个魔术方法“__clone()” class Person2 { private $name; private $sex; private $age; function __construct($name = "", $sex = "",$age=1) { $this->name = $name; $this->sex = $sex; $this->age = $age; } //声明此方法则在对象克隆时自动调用,用来为新对象重新赋值 function __clone(){ $this->name = "我是". $this->name. "的副本"; $this->age = 10; } function say(){ echo "我的名字: ".$this->name.", 性别: ".$this->sex.", 年龄: ".$this->age."
"; } } $p1 = new Person("张三", "男", 20); $p2 = clone $p1; $p1 -> say(); $p2 -> say(); ?>

 

抽象类与接口

abstract function fun1();
abstract function fun2();

//只要在声明类时有一个方法是抽象方法,那么这个类就是抽象类,抽象类也要使用abstract关键字修饰。在抽象类中可以有不是抽象的成员方法和成员属性,但访问权限不能使用private关键字修饰为私有的
抽象类中可以定义非抽象方法

//接口中不能声明非抽象方法,不能在接口中声明变量,只能使用const关键字声明为常量的成员属性,而且接口中的所有成员都必须有public的访问权限

 

匿名类

php
$person = new class {
    function say() {
        echo "匿名类!";
    }
};

$person->say();
?>

php
$person1 = new class ('哈哈'){
    public $name;
    function  __construct($name) {
        $this->name = $name;
    }

    function say() {
        echo "匿名类: {$this->name}";
    }
};
$person1->say();
?>

//匿名类! 匿名类: 哈哈

 

php
class SomeClass {}  //声明一个类SomeClass作为父类
interface SomeInterface {}  //声明一个接口SomeInterface用匿名类实现
trait SomeTrait {}  //声明一个trait,导入到匿名类中


//使用var_dump()直接输出匿名类的对象
//声明一个匿名类,通过构造函数为成员属性$num赋初值
//声明匿名类时继承SomeClass类
//声明匿名类时实现接口SomeInterface
var_dump(new class(100) extends SomeClass implements SomeInterface {
    private $num;
    public function __construct($num)
    {
        $this->num = $num;
    }
    use SomeTrait;
});


?>

php
//匿名类还可以在一个类的内部方法中声明,当匿名类被嵌套进普通类后,不能访问这个外部类的
//private、protected方法或者属性。为了访问外部类protected属性或方法,匿名类可以继承此外部类
//为了使用外部类的private属性,必须经过构造器传进来

class Outer {
    private $prop = 1;
    protected $prop2 = 2;

    protected function func1(){
        return 3;
    }

    //声明一个外部类的成员方法,在方法返回内部匿名类对象
    public function func2(){
        //声明和返回匿名类对象
        //通过构造方法将外部类的私有成员,访问外部类的私有属性
        //通过继承外部类,访问外部类的私有成员
        return new class($this->prop) extends Outer {
            private $prop3;  //内部类的私有成员

            public function __construct($prop)
            {
                $this->prop3 = $prop;
            }

            public function func3() {
                return $this->prop2 + $this->prop3 + $this->func1();
            }
        };
    }
}

//访问内部匿名类实例中的func3()
echo (new Outer) -> func2() ->func3();
?>

 

多态:同一类的对象收到相同消息时,会得到不同的结果,而这个消息是不可预测的。多态,就是多种状态,多种结果

多态是一种通过多种状态或阶段描述相同对象的编程方式。它的真正意义在于:实际开发中,只要关心一个接口或基类的编程,而不必关心一个对象所属于的具体类

//通过判断对象的类属性实现多态
php class employee { protected function working() { echo '本方法需重载才能运行'; } } class teacher extends employee { public function working() { echo '教书'; } } class coder extends employee { public function working() { echo '敲代码'; echo "
"; } } function dprint($obj) { if (get_class($obj) == 'employee') { echo 'Error'; } else { $obj->working(); } } dprint(new teacher()); dprint(new coder()); dprint(new employee());
//通过接口实现多态

php
interface employee {
    public function working();
}

class teacher implements employee {
    public function working() {
        echo '教书';
    }
}

class coder implements employee {
    public function working()
    {
        echo '敲代码';
    }
}

function dprint(employee $i) {
    $i -> working();
}

$a = new teacher;
$b = new coder();
dprint($a);
dprint($b);

PHP中父类和子类存在继承关系,但不存在血缘关系,子类无法向上转型为父类。

 

面向接口编程

接口本身什么也不做,系统在内部实现了接口的行为,所以只要实现了这个接口,就可以使用接口提供的方法,这就是接口“即插即用”思想

//Traits和接口很像,不同的是Traits可以导入包含代码的接口,Traits和接口都是对“多重继承”的一种变相实现
php
trait Hello {
    public function sayHello() {
        echo 'Hello';
    }
}

trait World {
    public function sayWorld() {
        echo 'World';
    }
}

class MyHelloWorld {
    use Hello, World;
    public function sayExclamationMark() {
        echo '!';
    }
}

$o = new MyHelloWorld();
$o -> sayHello();
$o -> sayWorld();
$o -> sayExclamationMark();
?>

 

php
trait DemoTrait {
    public $property1 = true;
    static $property2 = 1;
    function method1() {
        //codes
    }
    abstract public function method2();
}

/**
trait的基本使用:Trait不能通过它自身来实例化对象,必须将其混入类中使用。相当于将Trait中的成员复制到类中,在应用类时就像使用自己的
 * 成员一样
 */
trait Demo1_trait {
    function method1() {

    }

    function method2() {
        
    }
}

class Demo1_class {
    use Demo1_trait;
}

$obj = new Demo1_class();

$obj->method1();
$obj->method2();

?>
php

trait Demo1_trait {
    function func() {
        echo "第一个Trait中的func方法";
    }
}

trait Demo2_trait {
    function func() {  //两个同名方法有冲突
        echo "第二个Trait中的func方法";  
    }
}

class Demo_class {
    use Demo1_trait, Demo2_trait {
        Demo1_trait::func insteadof Demo2_trait;  //Demo2_trait中声明的在这里声明使用Demo1_trait的func替换
    }
}

$obj = new Demo_class();

$obj->func();

?>

为了对使用的类施加强制要求,Trait支持抽象方法的使用。如果在Trait中声明需要实现的抽象方法,这样就使得使用它的类必须先实现它

 

注意:

  • Trait会覆盖调用类继承的父类方法
  • 从基类继承的成员被Trait插入的新成员所覆盖。来自当前类的成员覆盖了Trait的方法,而Trait则覆盖了被继承的方法
  • Trait不能像类一样使用new实例化对象
  • 单个Trait可由多个Trait组成
  • 在单个类中,用use引入Trait,可以引入多个
  • Trait支持修饰词,例如final、static、abstract
  • 可以使用insteadof及as操作符解决Trait之间的冲突
  • 使用as语法还可以用来调整方法的访问控制

 

 

反射

面向对象编程中对象被赋予了自省的能力,而这个自省的过程就是反射

动态获取信息以及动态调用对象方法的功能称为反射API

php
class person {
    public $name;
    public $gender;
    public function say() {
        echo $this->name, "\tis", $this->gender, "\r\n";
    }
    public function __set($name, $value) {
        echo "Setting $name to $value \r\n";
        $this -> $name = $value;
    }
    public function __get($name) {
        if (!isset($this -> $name)) {
            echo '未设置';
            $this -> $name="正在设置默认值";
        }
        return $this -> $name;
    }
}

$student = new person();
echo $student -> name;
$student -> name = "tom";
$student -> gender = "male";
$student -> age = 24;
$student -> say();
echo $student -> name;
print "
"; //获取对象属性列表 $reflect = new ReflectionObject($student); $props = $reflect->getProperties(); foreach ($props as $prop) { print $prop -> getName()."\n"; print "
"; // print "
";
} print "
"; //获取对象方法列表 $m = $reflect->getMethods(); foreach ($m as $prop) { print $prop->getName()."\n"; print "
"; } //也可以使用class函数,返回对象属性的关联数组以及更多的信息 //返回对象属性的关联数组 var_dump(get_object_vars($student)); //类属性 var_dump(get_class_vars(get_class($student))); //返回由类的方法名组成的数组 var_dump(get_class_methods(get_class($student))); //获取对象属性列表所属的类 echo get_class($student); ------------------------------ Setting age to 24 tom ismale tom name gender age say __set __get array(3) { ["name"]=> string(3) "tom" ["gender"]=> string(4) "male" ["age"]=> int(24) } array(2) { ["name"]=> NULL ["gender"]=> NULL } array(3) { [0]=> string(3) "say" [1]=> string(5) "__set" [2]=> string(5) "__get" } person
//反射获取类的原型
$obj = new ReflectionClass('person');
$className = $obj -> getName();
$Methods = $Properties = array();
foreach ($obj -> getProperties() as $v)
{
    $Properties[$v->getName()] = $v;
}
foreach($obj->getMethods() as $v){
    $Methods[$v->getName()] = $v;
}

echo "class {$className}
{
"; is_array($Properties)&&ksort($Properties); foreach($Properties as $k => $v) { echo "\t"; echo $v -> isPublic()? 'public' : '', $v -> isPublic()? 'private':'', $v -> isProtected()? 'protected':'', $v->isStatic()? 'static' :' '; echo "{$k}
"; } echo "\n"; if(is_array($Methods)) ksort($Methods); foreach($Methods as $k => $v) { echo "function{$k}(){}
"; } echo "}
";

 

异常和错误处理

php
class emailException extends exception {

}

class pwdException extends exception {
    function __toString()
    {
        return "
Exception{$this->getCode()}: {$this->getMessage()} in File:{$this->getFile()} on line:{$this->getLine()}
"; //改写抛出异常结果 } } //异常分发 function reg($reginfo=null){ if (empty($reginfo) || isSet($reginfo)) { throw new Exception("参数非法"); } if (empty($reginfo['email'])){ throw new emailException("邮件为空"); } if ($reginfo['pwd'] != $reginfo['repwd']) { throw new pwdException("两次密码不一致"); } echo "注册成功"; } //对异常进行分拣并做处理 try { reg(array('email'=>'[email protected]', 'pwd'=>123456, 'repwd'=>12345678)); } catch(emailException $ee) { echo $ee -> getMessage(); } catch(pwdException $ep) { echo $ep; echo PHP_EOL, '特殊处理'; } catch(Exception $e) { echo $e->getTraceAsString(); echo PHP_EOL, '其他情况,统一处理'; }
php
    try{
        //可能出错的代码段
        if (文件上传不成功) throw(上传异常);
        if (插入数据库不成功) throw(数据库操作异常);
    } catch(异常){
        必须的补救措施,如删除文件、删除数据库插入记录
    }
?>



php
    上传{
        if(文件上传不成功) throw(上传异常);
        if(插入数据库不成功) throw(数据库操作异常);
    }

    //其他代码...
    try{
        上传;
        其他;
    } catch(上传异常) {
        必须的补救措施,如删除文件,删除数据库插入记录
    } catch(其他异常){
        记录log
    }
?>

 

错误处理机制

PHP里有一套错误处理机制,可以使用set_error_handler接管PHP错误处理,也可以使用trigger_error函数主动抛出一个错误

set_error_handler函数设置用户自定义的错误处理函数,函数用于创建运行期间的用户自己的错误处理方法。它需要先创建一个错误处理函数,然后设置错误级别

PHP程序的错误发生一般归属于:语法错误、运行时错误、逻辑错误

 

trigger_error()函数和die()函数

//set_error_handler()函数设置用户自定义的错误处理函数。函数用于创建运行期间的用户自己的错误处理方法。它需要先创建一个错误处理函数,然后设置错误级别
set_error_handler(error_function, error_types)
error_function:规定发生错误时运行的函数
error_types:规定在哪个错误报告级别会显示用户定义的错误,默认为“E_ALL

 

命名空间

解决重名问题。命名空间将代码划分出不同的区域,每个区域的常量、函数和类的名字互不影响

在命名空间里,define的作用是全局的,const则作用于当前空间

//独立的命名空间使用namespace关键字声明
php
namespace MyProject;


?>

//namespace需要写在PHP脚本的顶部,必须是第一个PHP指令(declare除外)

php
namespace MyProject1;

const TEST = 'this is a const';

function demo(){
    echo "this is a function";
}

class User {
    function fun() {
        echo "this is User's fun()";
    }
}

echo TEST;
demo();

namespace MyProject2;

const TEST2 = "this is MyProject2 const";
echo TEST2;

//调用MyProject1空间中的demo函数
\MyProject1\demo();

//使用MyProject1空间中的类实例化对象
$user = new \MyProject1\User();
$user->fun();

?>

命名空间的子空间和公共空间

 

你可能感兴趣的:(面向对象思想的核心概念)