刘华世的官方博客

PHP 类与对象

类与对象


基本概念

每个类的定义都是关键class开头。

<?php
namespace NS
{
    class A
    {
        // 成员属性
        public $temp = '成员属性';

        // 方法
        public function displayVar()
        {
            // 使用伪变量$this调用成员属性
            echo $this->temp . "<br>";
        }
    }
// 使用new关键字实例化一个类
    $temp = new A();
    echo $temp->displayVar();

    class B extends A
    {
        // 构造方法
        function __construct()
        {
            $this->temp = '成员属性B继承于A';
        }
    }
    $temp = new B();
    echo $temp->displayVar();

    echo A::class;
}
?>

访问控制

public(公有),protected(受保护)或 private(私有)。

成员变量使用

可以用 ->(对象运算符):$this->property(其中 property 是该属性名)这种方式来访问非静态属性。 静态属性则是用 ::(双冒号):self::$property 来访问。

类常量

<?php
class MyClass
{
    const constant = 'constant value';

    function showConstant() {
        echo  self::constant . "\n";
    }
}

echo MyClass::constant . "\n";

$classname = "MyClass";
echo $classname::constant . "\n"; // 自 5.3.0 起

$class = new MyClass();
$class->showConstant();

echo $class::constant."\n"; // 自 PHP 5.3.0 起
?>

类的自动加载

spl_autoload_register() 函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载。

本例尝试分别从 MyClass1.php 和 MyClass2.php 文件中加载 MyClass1 和 MyClass2 类。

<?php
spl_autoload_register(function ($class_name) {
    require_once $class_name . '.php';
});

$obj  = new MyClass1();
$obj2 = new MyClass2();
?>

构造函数

<?php
class BaseClass {
   function __construct() {
       print "In BaseClass constructor\n";
   }
}

class SubClass extends BaseClass {
   function __construct() {
       parent::__construct();
       print "In SubClass constructor\n";
   }
}

class OtherSubClass extends BaseClass {
    // inherits BaseClass's constructor
}

// In BaseClass constructor
$obj = new BaseClass();

// In BaseClass constructor
// In SubClass constructor
$obj = new SubClass();

// In BaseClass constructor
$obj = new OtherSubClass();
?>

析构函数

<?php
class MyDestructableClass {
   function __construct() {
       print "In constructor\n";
       $this->name = "MyDestructableClass";
   }

   function __destruct() {
       print "Destroying " . $this->name . "\n";
   }
}

$obj = new MyDestructableClass();
?>

Static(静态)关键字

由于静态方法不需要通过对象即可调用,所以伪变量 $this 在静态方法中不可用。

静态属性不可以由对象通过 -> 操作符来访问。

用静态方式调用一个非静态方法会导致一个 E_STRICT 级别的错误。

抽象类

任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。

<?php
abstract class AbstractClass
{
 // 强制要求子类定义这些方法
    abstract protected function getValue();
    abstract protected function prefixValue($prefix);

    // 普通方法(非抽象方法)
    public function printOut() {
        print $this->getValue() . "\n";
    }
}

class ConcreteClass1 extends AbstractClass
{
    protected function getValue() {
        return "ConcreteClass1";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass1";
    }
}

class ConcreteClass2 extends AbstractClass
{
    public function getValue() {
        return "ConcreteClass2";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass2";
    }
}

$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";

$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') ."\n";
?>

对象接口

使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。

接口是通过 interface 关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的。

接口中定义的所有方法都必须是公有,这是接口的特性。

实现(implements)

要实现一个接口,使用 implements 操作符。类中必须实现接口中定义的所有方法,否则会报一个致命错误。 类可以实现多个接口,用逗号来分隔多个接口的名称。

Trait代码复用

<?php

class A
{
    public function sayHello()
    {
        echo "Hello ";
    }
}

trait mytrait
{
    public function traitFunction()
    {
        parent::sayHello();
        echo "world!";
    }

}

class B extends A
{
    use mytrait;
}

$temp = new B();
$temp->traitFunction();

匿名类

PHP 7 开始支持匿名类。

<?php

// PHP 7 之前的代码
class Logger
{
    public function log($msg)
    {
        echo $msg;
    }
}

$util->setLogger(new Logger());

// 使用了 PHP 7+ 后的代码
$util->setLogger(new class {
    public function log($msg)
    {
        echo $msg;
    }
});

魔术方法

public void __set ( string $name , mixed $value )

public mixed __get ( string $name )

public bool __isset ( string $name )

public void __unset ( string $name )

在给不可访问属性赋值时,__set() 会被调用。

读取不可访问属性的值时,__get() 会被调用。

当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。

当对不可访问属性调用 unset() 时,__unset() 会被调用。

参数 $name 是指要操作的变量名称。__set() 方法的 $value 参数指定了 $name 变量的值。

属性重载只能在对象中进行。在静态方法中,这些魔术方法将不会被调用。 所以这些方法都不能被 声明为 static。从 PHP 5.3.0 起, 将这些魔术方法定义为 static 会产生一个警告。

遍历对象

PHP 5 提供了一种定义对象的方法使其可以通过单元列表来遍历, 例如用 foreach 语句。默认情况下,所有可见属性都将被用于遍历。

<?php

class MyClass
{
    public $var1 = 'value 1';
    public $var2 = 'value 2';
    public $var3 = 'value 3';

    protected $protected = 'protected var';
    private $private = 'private var';

    function iterateVisible()
    {
        echo "MyClass::iterateVisible:<br>";
        foreach ($this as $key => $value)
        {
            print '$key=>' . $key . "<br>";
            print '$value=>' . $value . "<br>";
        }
    }
}

$class = new MyClass();
$class->iterateVisible();

foreach ($class as $key => $value)
{
    print '$key=>' . $key . "<br>";
    print '$value=>' . $value . "<br>";
}

Final 关键字

PHP 5 新增了一个 final 关键字。如果父类中的方法被声明为 final,则子类无法覆盖该方法。 如果一个类被声明为 final,则不能被继承。

<?php

class BaseClass
{
    public function display()
    {
        echo 'final';
    }

    final public function test()
    {
        echo 'Hello World!';
    }
}

class A extends BaseClass
{
    public function display()
    {
        echo 'A';
    }

    public function test()
    {
        echo 'haha';
    }
}

$temp = new A();
$temp->test();
#会报错,final方法不能重写

对象复制

在多数情况下,我们并不需要完全复制一个对象来获得其中属性。但有一个情况下确实需要:如果你有一个 GTK 窗口对象,该对象持有窗口相关的资源。

你可能会想复制一个新的窗口,保持所有属性与原来的窗口相同,但必须是一个新的对象(因为如果不是新的对象,那么一个窗口中的改变就会影响到另一个窗口)。

还有一种情况:

如果对象 A 中保存着对象 B 的引用,当你复制对象 A 时,你想其中使用的对象不再是对象 B 而是 B 的一个副本,那么你必须得到对象 A 的一个副本。

对象比较

当使用比较运算符(==)比较两个对象变量时,比较的原则是:

如果两个对象的属性和属性值 都相等,而且两个对象是同一个类的实例,那么这两个对象变量相等。

而如果使用全等运算符(===),这两个对象变量一定要指向某个类的同一个实例(即同一个对象)。

类型约束

PHP 5 可以使用类型约束。

如果一个类或接口指定了类型约束,则其所有的子类或实现也都如此。

类型约束不能用于标量类型如 int 或 string。Traits 也不允许。

<?php

class MyClass
{
    public function test_class(OtherClass $otherclass)
    {
        echo $otherclass->var;
        echo "<br>";
    }

    public function test_array(array $input_array)
    {
        print_r($input_array);
    }
}

class OtherClass
{
    public $var = 'Hello World!';
}

$myclass    = new MyClass();
$otherclass = new OtherClass();
$myclass->test_class($otherclass);

$input_array = ['one' => 'first', 'two' => 'second'];
$myclass->test_array($input_array);

后期静态绑定

自 PHP 5.3.0 起,PHP 增加了一个叫做后期静态绑定的功能,用于在继承范围内引用静态调用的类。

self:: 的限制

使用 self:: 或者 CLASS 对当前类的静态引用,取决于定义当前方法所在的类:

后期静态绑定的用法

<?php

class A
{
    public static function who()
    {
        echo __CLASS__;
    }
    public static function test()
    {
//        self::who(); // 输出A
        static::who(); // 输出B
    }
}

class B extends A
{
    public static function who()
    {
        echo __CLASS__;
    }
}

$temp = new B();
$temp::test();

对象和引用

在php5 的对象编程经常提到的一个关键点是“默认情况下对象是通过引用传递的”。

但其实这不是完全正确的。下面通过一些例子来说明。

PHP 的引用是别名,就是两个不同的变量名字指向相同的内容。 在 PHP 5,一个对象变量已经不再保存整个对象的值。只是保存一个标识符来访问真正的对象内容。 当对象作为参数传递,作为结果返回,或者赋值给另外一个变量,另外一个变量跟原来的不是引用的关系, 只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容。

<?php

class A
{
    public $foo = 1;

}

$a = new A();
$b = $a;

$b->foo = 2;
echo $a->foo . "\n"; // 输出为2

对象序列化

序列化对象 - 在会话中存放对象

所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。 unserialize()函数能够重新把字符串变回php原来的值。 序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。

<?php
//
//include("class.inc");
//
//$a = new A();
//$a->show_one();
//
//$s = serialize($a);
//file_put_contents('store', $s);

// 反序列化必须要包含此文件。
include("class.inc");
$s = file_get_contents('store');
$a = unserialize($s);

$a->show_one();

命名空间

  1. 解决PHP内部的类、函数、常量或第三方类、函数、常量之间的名字冲突。
  2. 为很长的标识名称创建一个别名,提高源代码的可读性。
<?php

namespace my\name;

class MyClass{}

function myfunction(){}

const MYCONST = 1;

$a = new MyClass();
$c = new \my\name\MyClass;
$a = strlen('hi');
$d = namespace\MYCONST;
$d = __NAMESPACE__ . '\MYCONST';
echo $d;
echo constant($d);

定义命名空间

虽然任意合法的PHP代码都可以包含在命名空间中,但只有以下类型的代码受命名空间的影响,它们是:

类、接口、函数和常量。

命名空间通过关键字namespace来声明。如果一个文件中包含命名空间,它必须在其它代码之前声明命名空间, 有一个除外:declare关键字。

<?php

namespace space\MyProject;

const CONNECT_OK = 1;

class Connection{}
function connect(){}

echo __NAMESPACE__;

在同一个文件中定义多个命名空间

在同一个文件中定义多个命名空间有两种语法形式。

不建议使用这种语法在单个文件中定义多个命名空间,区间会显得不明确。

<?php

namespace MyProject;

const CONNECT_OK = 1;
class Connection
{
}

function connect()
{
}

echo __NAMESPACE__ . "<br>";

namespace AnotherProject;

const CONNECT_OK = 1;
class Connection
{
}

function connect()
{
}

echo __NAMESPACE__ . "<br>";
// 不建议使用这种语法在单个文件中定义多个命名空间,区间会显得不明确。

使用大括号来定义多个命名空间

<?php

namespace MyProject
{
    const CONNECT_OK = 1;
    class Connection
    {
    }

    function connect()
    {
    }

    echo __NAMESPACE__ . "<br>";
}

namespace AnotherProject
{
    const CONNECT_OK = 1;
    class Connection
    {
    }

    function connect()
    {
    }

    echo __NAMESPACE__ . "<br>";

}

使用命名空间:别名、导入

允许通过别名引用或导入外部的完全限定名称,是命名空间的一个重要特征。

<?php

namespace MyProject
{
    const CONNECT_OK = 1;
    class Connection
    {
        public function __construct()
        {
            echo "MyProject __construct" . "<br>";
        }
    }

    function connect()
    {
        echo "MyProject connect" . "<br>";
    }

    echo __NAMESPACE__ . "<br>";
}

namespace AnotherProject
{
    const CONNECT_OK = 1;
    class Connection
    {
        public function __construct()
        {
            echo "AnotherProject __construct" . "<br>";
        }
    }

    function connect()
    {
        echo "AnotherProject connect" . "<br>";
    }

    $temp = new \MyProject\Connection();
    use \MyProject as A;
    $temp = new A\Connection();
    use \Myproject\Connection as AConnection;
    $temp = new AConnection();

    echo __NAMESPACE__ . "<br>";
}

全局空间

如果没有定义任何命名空间,所有的类与函数的定义都是在全局空间,与PHP引入命名空间概念前一样。

在名称前加上前缀"\"表示该名称是全局空间中的名称,即使该名称位于其它的命名空间中时也是如此。

<?php

namespace www\web;

class MyClass
{
    public function display()
    {
        try
        {
            echo 'Hello World!';
        }
        catch (\Exception $e)
        {
            echo 'try...catch';
        }
    }
}

$temp = new MyClass();
$temp->display();

使用命名空间:后备全局函数/常量

在一个命名空间中,当PHP遇到一个非限定的类、函数或常量名称时,它使用不同的优先策略来解析该名称。

类名称总是解析到当前命名空间中的名称。因此在访问系统内部或不包含在命名空间中的类名称时,必须使用完全限定名称。

对于函数和常量来说,如果当前命名空间中不存在该函数或常量,PHP会退而使用全局空间中的函数或常量。

<?php

namespace A\B\C;

class Exception extends \Exception
{
}

$a = new Exception('message');
$b = new \Exception('message');

echo strlen('Hello World!'); // 正常输出12
$c = new ArrayObject(); // 致命错误,找不到A\B\C\ArrayObject类

命名空间名称解析规则

命名空间名称定义

  • 非限定名称Unqualified name

名称中不包含命名空间分隔符的标识符,例如Foo

  • 限定名称Qualified name

名称中含有命名空间分隔符的标识符,例如Foo\Bar

  • 完全限定名称Fully qualified name

名称中包含命名空间分隔符,并以命名空间分隔符开始的标识符,例如\Foo\Bar。

生成器

生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现Iterator接口的方式,性能开销和复杂性大大降低。

生成器允许你在foreach代码块中写代码来迭代一组数据而不需要在内存中创建一个数组,在内存中创建一个大数组可能会使你的内存达 到上限,或者会占据可观的处理时间。

生成器函数,就像一个普通的自定义函数一样,和普通函数只返回一次不同的是,生成器可以根据需要yield多次,以便生成需要迭代的值。

一个简单的例子就是使用生成器来重新实现 range() 函数。 标准的 range() 函数需要在内存中生成一个数组包含每一个在它范围内的值, 然后返回该数组, 结果就是会产生多个很大的数组。 比如,调用 range(0, 1000000) 将导致内存占用超过 100 MB。

<?php

function xrange($start, $end, $step = 1)
{
    for ($i = $start; $i <= $end; $i += $step)
    {
        yield $i;
    }
}
// 直接报500错误
foreach (range(0, 1000000) as $val)
{
    echo $val;
}
// 进入大循环
foreach (xrange(0, 1000000) as $val)
{
    echo $val;
}

生成器语法

我的理解就是链表来的,一个指针像游标一样,循环迭代。

一个生成器函数看起来像一个普通的函数,不同的是普通函数返回一个值,而一个生成器可以yield生成许多它所需要的值。

当一个生成器被调用的时候,它返回一个可以被遍历的对象。当你遍历这个对象的时候PHP将会在每次需要值的时候调用生成器函数, 并在产生一个值之后保存生成器的状态,这样它就可以在需要产生下一个值的时候恢复调用状态。

yield关键字

生成器函数的核心就是yield关键字。它最简单的调用形式看起来像一个return申明,不同之处在于普通return会返回值并 终止函数的执行,而yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。

<?php

function test()
{
    for ($i = 0; $i <= 10; $i++)
    {
        // 注意:变量$i的值在不同的yield之间是保持传递的。
        yield $i;
    }
}

$temp = test();
foreach ($temp as $value)
{
    echo "$value" . "<br>";
}

指定键名来生成值

<?php

$input = <<<'EOF'
1;PHP;Likes dollar signs
2;Python;Likes whitespace
3;Ruby;Likes blocks
EOF;

function input_parse($input)
{
    foreach (explode("\n", $input) as $line)
    {
        $fields = explode(';', $line);
        $id     = array_shift($fields); // array_shift将数组开头的单元移出数组

        yield $id => $fields;
    }
}

foreach (input_parse($input) as $key => $value)
{
    echo '$key=>' . $key . "<br>";
    echo '$value=>' . print_r($value) . "<br>";
}

生成null值

yield可以在没有参数传入的情况下被调用来生成一个NULL值并配对一个自动的键名。

可以使用生成器来初始化一个null数组。

<?php

function test()
{
    foreach (range(1, 3) as $i)
    {
        yield;
    }
}
var_dump(iterator_to_array(test()));

使用引用来生成值

生成函数可以像使用值一样来使用引用生成。

<?php

function &test()
{
    $value = 10;

    while ($value > 0)
    {
        yield $value;
    }
}

foreach (test() as &$value)
{
    echo (--$value) . '...';
}

PHP中的引用

在PHP中引用意味着用不同的名字访问同一个变量内容。这并不像C的指针: 例如你不能对他们做指针运算,他们并不是实际的内存地址。

在PHP中,变量名和变量的内容是不一样的,因此同样的内容可以有不同的名字。

$a = 'Hello world!';
$b = &$a;
$b = 'new Hello world!';
//echo $a;

function test(&$temp){
    $temp = 'function Hello world!';
}
test($b);
echo $a;

引用返回

引用返回用在当想用函数找到引用应该被绑定在哪一个变量上面时。

不要用返回引用来增加性能,引擎足够聪明来自己进行优化。

仅在有合理的技术原因时才返回引用!要返回引用,请看如下示例:

class temp
{
    public $value = 100;

    public function &getValue()
    {
        return $this->value;
    }
}

$obj   = new temp();
$value = &$obj->getValue();
echo $value . '<br>'; // 输出100
$obj->value = 9;
echo $value . '<br>'; // 输出9

取水引用

当unset一个引用,只是断开了变量名和变量内容之间的绑定。这并不意味着变量内容被销毁了。

$a = 1;
$b = &$a;
unset($a);

引用定位

许多PHP的语法结构 是通过引用机制实现的,所以上述有关引用绑定的一切也都适用于这些结构。

global引用

当用global $var声明一个变量时实际上建立了一个到全局变量的引用。

global $var;
$var = & $GLOBALS['var']; // 与上一行代码是等价的。

$this

在一个对象的方法中,$this永远是调用它的对象的引用。

php如何使用类

class myclass{
    //成员变量(公有访问控制)
    public $myvar;
    //构造函数
    function __construct($myvar){
        $this->myvar = $myvar;
    }
    //封装
    function setValue($myvar){
        $this->myvar = $myvar;
    }
    function getValue(){
        return $this->myvar;
    }
    //析构函数
    function __destruct(){
        unset($this->myvar);
    }
}
//类继承
class myextends extends myclass{
}
$myclass = new myextends("Moments");
echo $myclass->getValue();
  • 如何使用静态变量
class myclass{
    //使用静态变量
    public static $myvar = "Moments";
    public function myfun(){
        return self::$myvar;
    }
}
$myclass = new myclass();
echo myclass::$myvar;
echo $myclass->myfun();        
  • 如何使用抽象类
abstract class myclass{
    public $myvar;
    //强制要求子类定义之些方法
    abstract protected function setValue($myvar);
    //普通方法
    public function getValue(){
        return $this->myvar;
    }
}
class myextends extends myclass{
    function setValue($myvar){
        $this->myvar = $myvar;
    }
    function getValue(){
        return parent::getValue();
    }
}
$myclass = new myextends();
$myclass->setValue("Moments");
echo $myclass->getValue();      
  • 如何使用对象接口
#接口中定义的所有方法都必须是公有
interface myinterface{
    //接口不支持成员变量,可以使用常量
    const myconst = "myconst";
    //类中必须实现接口中定义的所有方法。
    public function setValue($myvar);
    public function getValue();
}
class myextends implements myinterface{
    public function setValue($myvar){
        $this->myvar = $myvar;
    }
    public function getValue(){
        return $this->myvar;
    }
}
$myclass = new myextends();
$myclass->setValue("Moments");
echo $myclass->getValue();
echo myinterface::myconst;

 

衿华客

php 教程

  1. PHP简介
  2. PHP安装
  3. PHP语法
  4. PHP数据类型
  5. PHP变量
  6. PHP运算符
  7. PHP流程控制
  8. PHP函数
  9. PHP类与对象
  10. PHP字符串
  11. PHP正则表达示
  12. PHP文件
  13. PHP图形图像
  14. PHPXML
  15. PHP会话缓存
  16. PHP异常处理
  17. PHP数据库
  18. PHP日期时间戳
  19. PHP代码风格规范

server 环境

  1. ubuntu入门
  2. docker入门
  3. git入门
  4. iterm入门

frame 框架

  1. laravel入门
  2. dingo入门
  3. JWT入门
  4. Laravel框架关键技术解析
  5. laravel则修笔记
  6. rpc入门
  7. rabbitmq入门
  8. opencc多国语言
  9. laravel兄弟连笔记
  10. laravel进阶

case 实例

  1. php面试题笔记
  2. python图标提取
  3. 字幕乱码问题
  4. 支付宝接口调用
  5. laravel开发笔记

db 数据库

  1. elasticsearch入门
  2. 数据库操作
  3. 数据库设计
  4. redis入门

help

posted @ 2017-07-10 13:53  刘华世的博客  阅读(528)  评论(0编辑  收藏  举报
刘华世的官方博客