每一个类的定义都是关键class开头。php
<?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(私有)。python
能够用 ->(对象运算符):$this->property(其中 property 是该属性名)这种方式来访问非静态属性。 静态属性则是用 ::(双冒号):self::$property 来访问。laravel
<?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)时自动去加载。git
本例尝试分别从 MyClass1.php 和 MyClass2.php 文件中加载 MyClass1 和 MyClass2 类。web
<?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(); ?>
因为静态方法不须要经过对象便可调用,因此伪变量 $this 在静态方法中不可用。面试
静态属性不能够由对象经过 -> 操做符来访问。redis
用静态方式调用一个非静态方法会致使一个 E_STRICT 级别的错误。docker
任何一个类,若是它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。数据库
<?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 操做符。类中必须实现接口中定义的全部方法,不然会报一个致命错误。 类能够实现多个接口,用逗号来分隔多个接口的名称。
<?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>"; }
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:: 或者 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();
<?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类
名称中不包含命名空间分隔符的标识符,例如Foo
名称中含有命名空间分隔符的标识符,例如Foo\Bar
名称中包含命名空间分隔符,并以命名空间分隔符开始的标识符,例如\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关键字。它最简单的调用形式看起来像一个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>"; }
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中引用意味着用不一样的名字访问同一个变量内容。这并不像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 $var声明一个变量时实际上创建了一个到全局变量的引用。
global $var; $var = & $GLOBALS['var']; // 与上一行代码是等价的。
在一个对象的方法中,$this永远是调用它的对象的引用。
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;