Home  >  Article  >  Backend Development  >  Classes and Objects - PHP Manual Notes

Classes and Objects - PHP Manual Notes

WBOY
WBOYOriginal
2016-08-08 09:29:10828browse

Basic concepts

PHP treats objects the same way as references and handles, that is, each variable holds a reference to the object rather than a copy of the entire object.

When a new object is created, the object is always assigned a value, unless the object defines a constructor and an exception is thrown on error. Classes should be defined before being instantiated.

When creating an object, if the class belongs to a namespace, its full name must be used.

Inside the class definition, objects can be created using new self and new parent.

<code><?php 
$instance = new stdClass();
$assigned = $instance;
$reference = & $instance;
$instance->var = '$assigned will have this value.';
$instance = null;
var_dump($instance);
var_dump($reference);
var_dump($assigned);</code>

The output of this code is as follows. Why is this?

<code>null
null
object(stdClass)[1]
  public 'var' => string '$assigned will have this value.' (length=31)</code>

PHP 5.3 introduces two new methods to create an instance of an object. Instances can be created using the following methods.

<code><?php 
class Test {
	static public function getNew() {
		return new static;
	}
}
class Child extends Test {}
$obj1 = new Test();
$obj2 = new $obj1;
var_dump($obj1 !== $obj2);  // true
$obj3 = Test::getNew();
var_dump($obj3 instanceof Test);  // true
$obj4 = Child::getNew();
var_dump($obj4 instanceof Child);  // true
var_dump($obj1 == $obj2);  // true</code>

PHP does not support multiple inheritance. Inherited methods and properties can be overridden by redeclaring them with the same name. Note that the parameters must remain consistent, except for the constructor of course. But if the parent class uses final when defining a method, the method cannot be overridden. Overridden methods and properties can be accessed through parent::. parent:: can only access constants const in the parent class, but not variables.

<code><?php 
class A {
	private $name = &#39;A&#39;;
	const conname = &#39;A&#39;;
	public function getName() {
		return $this->name;
	}
}
class B extends A {
	private $name = 'B';
	const conname = 'B';
	public function getName() {
		return $this->name;
	}
	public function getParent() {
		return parent::conname;
	}
}
class C extends B {
	private $name = 'C';
	const conname = 'C';
	public function getName() {
		return $this->name;
	}
	public function getParent() {
		return parent::conname;
	}
}
$a = new A;
var_dump($a->getName());  // A
$b = new B;
var_dump($b->getName());  // B
var_dump($b->getParent());  // A
$c = new C;
var_dump($c->getName());  // C
var_dump($c->getParent());  // B</code>

Since PHP 5.5, the keyword class can also be used for class name resolution. Using ClassName::class you can get a string containing the fully qualified name of a class ClassName.

<code><?php 
namespace NS {
	class ClassName {}
	echo ClassName::class;  // NS\ClassName
}</code>

Properties

Attributes, which are variable members of the class. Variables in properties can be initialized, but the initialized value must be a constant. The constant here means that the PHP script can get its value during the compilation phase, and does not rely on runtime information to evaluate.

In the member method of the class, use $this->property to access non-static properties, and use self::$property to access static properties. Use the static keyword when declaring static properties.

Class constants

No need for $ symbols and access control keywords when defining constants.

Constants can also be defined in interfaces.

Automatic loading of classes

When writing object-oriented applications, it is common to have one PHP source file for each class definition. When a file needs to call these classes, a long list of included files needs to be written at the beginning of the file. In fact, this is not necessary. You can define a __autoload() function, which will be automatically called when trying to use a class that has not yet been defined.

The manual tip says that spl_autoload_register() provides a more flexible way to implement automatic loading of classes. We will look at this later.

Automatic loading is not available in PHP's CLI interactive mode, which is the command line mode.

There may be dangerous characters in user input. At least the input must be verified during __autoload().

Classes can be loaded automatically in the following way.

<code><?php 
function __autoload($class_name) {
	require_once $class_name.&#39;.php&#39;;
}
$obj1 = new MyClass1();
$obj2 = new MyClass2();</code>

For exception handling, we will look at it later.

Constructor and Destructor

PHP 5 allows developers to define a method as a constructor in a class, and the constructor does not support overloading.

If a constructor is defined in the subclass, the constructor of the parent class will not be called implicitly, otherwise it will be inherited from the parent class like a normal class method (provided it is not defined as private). To execute the parent class's constructor, you need to call parent::__construct() in the child class constructor.

Unlike other methods, when __construct() and the parent class __construct() have different parameters, they can be overridden.

Since PHP 5.3.3, in the namespace, methods with the same name as the class name are no longer used as constructors.

The destructor will be executed when all references to an object are deleted or the object is explicitly destroyed. The destructor is called even when the script is terminated using exit().

Attempting to throw an exception in the destructor will result in a fatal error.

Access Control

Class attributes must be defined as one of public, protected, and private, and the keyword cannot be omitted. If the method in the class does not set the access control keyword, the method will be public by default.

Objects of the same class, even if they are not the same instance, can access each other's private and protected members. The sample program is as follows.

<code><?php 
Class Test {
	private $foo;
	public function __construct($foo) {
		$this->foo = $foo;
	}
	private function bar() {
		echo 'Accessed the private method.';
	}
	public function baz(Test $other) {
		$other->foo = 'hello';
		var_dump($other->foo);
		$other->bar();
	}
}
$test = new Test('test');
$test->baz(new Test('other'));</code>

Object inheritance

If one class extends another, the parent class must be declared before the child class.

Scope parsing operator

The scope resolution operator, simply put, is a pair of colons, which can be used to access static members, class constants, and can also be used to call properties and methods in the parent class.

When referencing these items outside of the class definition, use the class name.

static

Use the static keyword to define static methods and properties, as well as to define static variables and late static binding. Declaring a class property or method static allows you to access it directly without instantiating the class.

Static properties cannot be accessed through an object whose class has been instantiated, but static methods can.

If no access control is specified, properties and methods default to public.

Calling a non-static method from a static method will result in an E_STRICT level error.

抽象类

PHP 5支持抽象类和抽象方法。类中如果有一个抽象方法,那这个类必须被声明为抽象的。

抽象类不能被实例化。抽象方法只是声明了其调用方式(参数),不能定义其具体的功能实现。继承抽象类时,子类必须定义父类中的所有抽象方法,且这些方法的访问控制必须和父类一样活更宽松。

方法的调用方式必须匹配。但是,子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。这也试用与PHP 5.4起的构造函数。可以在子类中定义父类签名中不存在的可选参数。

<code><?php 
abstract class AbstractClass {
	abstract protected function prefixName($name);
}
class ConcreteClass extends AbstractClass {
	public function prefixName($name, $separator = &#39;, &#39;) {
		if($name === "Pacman") {
			$prefix = &#39;Mr&#39;;
		} elseif($name === &#39;Pacwoman&#39;) {
			$prefix = "Mrs";
		} else {
			$prefix = &#39;&#39;;
		}
		return "$prefix $separator $name  ";
	}
}
$class = new ConcreteClass;
echo $class->prefixName('Pacman');
echo $class->prefixName('Pacwoman');</code>

对象接口

听说过接口,一直没用过。使用接口,可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容,也就是说接口中定义的所有方法都是空的。接口中定义的所有方法都必须是公有的,这是接口的特性。

接口也可以继承多个接口,用逗号分隔,使用extends操作符。类中必须实现接口中定义的所有方法,否则会报错。要实现一个接口,使用implements操作符。类可以实现多个接口,用逗号分隔。实现多个接口时,接口中的方法不能有重名。类要实现接口,必须使用和接口中所定义的方法完全一致的方式。

接口中也可定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口覆盖。

traits

从PHP 5.4.0开始,可以使用traits实现代码复用。Traits 是一种为类似 PHP 的单继承语言而准备的代码复用机制。Trait 不能通过它自身来实例化。它为传统继承增加了水平特性的组合。

优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。

通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中。如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误,为解决冲突,需使用insteadof操作符来指明使用冲突方法中的哪一个,这种方法仅允许排除掉其它方法。as操作符可以将其中一个冲突的方法以另一个名称(别名)来引入。

<code><?php 
trait A {
	public function smallTalk() {
		echo &#39;a&#39;;
	}
	public function bigTalk() {
		echo &#39;A&#39;;
	}
}
trait B {
	public function smallTalk() {
		echo &#39;b&#39;;
	}
	public function bigTalk() {
		echo &#39;B&#39;;
	}
}
class Talker {
	use A, B {
		B::smallTalk insteadof A;
		A::bigTalk insteadof B;
		B::bigTalk as talk;
	}
}
$t = new Talker();
$t->smallTalk();  // b
$t->bigTalk();  // A
$t->talk();  // B</code>

使用as操作符还可以用来调整方法的访问控制,或者给方法一个改变了访问控制的别名,原版方法的访问控制规则没有改变。

<code><?php 
trait HelloWorld {
	public function sayHello() {
		echo &#39;Hello World.&#39;;
	}
}
class MyClass1 {
	use HelloWorld {
		sayHello as protected;
	}
}
class MyClass2 {
	use HelloWorld {
		sayHello as private myPrivateHello;
	}
}</code>

就像类能够使用trait那样,多个trait能够组合为一个trait

为了对使用的类施加强制要求,trait 支持抽象方法的使用。

<code><?php 
trait Hello {
	public function sayHelloWorld() {
		echo &#39;Hello &#39; . $this->getWorld();
	}
	abstract public function getWorld();
}
class MyHelloWorld {
	private $world;
	use Hello;
	public function getWorld() {
		return $this->world;
	}
	public function setWorld($val) {
		$this->world = $val;
	}
}
$c = new MyHelloWorld;
$c->setWorld('world');
$c->sayHelloWorld();</code>

如果trait定义了一个属性,那类将不能定义同样名称的属性,否则会产生错误。

重载

PHP提供的重载是指动态地创建类属性和方法,与其它绝大多数面向对象语言不同。通过魔术方法来实现。当使用不可访问的属性或方法时,重载方法会被调用。所有的重载方法都必须被声明为public

使用__get()__set()__isset()__unset()进行属性重载,示例如下。

<code><?php 
class PropertyTest {
	private $data = array();
	public $declared = 1;
	private $hidden = 2;
	public function __set($name, $value) {
		echo "Setting $name to $value. " . &#39;<br>';
		$this->data[$name] = $value;
	}
	public function __get($name) {
		echo "Getting $name. <br>";
		if(array_key_exists($name, $this->data)) {
			return $this->data[$name];
		}
		return null;
	}
	public function __isset($name) {
		echo "Is $name set? <br>";
		return isset($this->data[$name]);
	}
	public function __unset($name) {
		echo "Unsetting $name. <br>";
		unset($this->data[$name]);
	}
}
$obj = new PropertyTest;
$obj->a = 1;
var_dump($obj->a);
var_dump(isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
var_dump($obj->declared);
var_dump($obj->hidden);</code>

输出结果如下:

<code>Setting a to 1. 
Getting a. 
int 1
Is a set? 
boolean true
Unsetting a. 
Is a set? 
boolean false
int 1
Getting hidden. 
null</code>

在对象中调用一个不可访问方法时,__call()会被调用。用静态方式中调用一个不可访问方法时,__callStatic()会被调用。参数为调用方法的名称和一个枚举数组,注意区分大小写。

使用__call()__callStatic()对方法重载,示例如下。

<code><?php 
class MethodTest {
	public function __call($name, $arguments) {
		echo "Calling object method $name " . 
			implode(', ', $arguments) . '<br>';
	}
	public static function __callStatic($name, $arguments) {
		echo "Calling static method $name " . 
			implode(', ', $arguments) . '<br>';
	}
}
$obj = new MethodTest;
$obj->runTest('in object context');
MethodTest::runTest('in static context');</code>

遍历对象

对象可以用过单元列表来遍历,例如用foreach语句。默认所有可见属性都将被用于遍历。

<code><?php 
class MyClass {
	public $var1 = &#39;value 1&#39;;
	public $var2 = &#39;value 2&#39;;
	public $var3 = &#39;value 3&#39;;
	private $var4 = &#39;value 4&#39;;
	protected $var5 = &#39;value 5&#39;;
}
$obj = new MyClass;
foreach($obj as $key => $value) {
	echo "$key => $value <br>";
}</code>

示例程序2实现了Iterator接口的对象遍历,示例程序3通过实现IteratorAggregate来遍历对象。

魔术方法

PHP 将所有以__(两个下划线)开头的类方法保留为魔术方法。定义类方法时,除魔术方法外,建议不要以__为前缀。

前面遇到过的魔术方法有:__construct()__destruct()__call()__callStatic()__get()__set()__isset()__unset()。后面将会介绍:__sleep()__wakeup()__toString()__invoke()__set_state()__clone()__debugInfo()

__sleep__wakeup不清楚具体做什么用的,示例程序中给出了个数据库连接的例子。

__toString方法用于一个类被当成字符串时应怎样回应。此方法必须返回一个字符串,且不能再方法中抛出异常。如果将一个未定义__toString()方法的对象转换为字符串,将产生错误。

当尝试以调用函数的方式调用一个对象时,__invoke()方法会被调用。

当调用var_export()导出类时,__set_state()会被调用。

当调用var_dump()时,__debugInfo会被调用。PHP 5.6新加入,没合适的环境无法测试。

final

果父类中的方法被声明为final,则子类无法覆盖该方法。如果一个类被声明为final,则不能被继承。属性不能被定义为final,只有类和方法才能被定义为final

对象复制

多数情况,我们不需要完全复制一个对象,但有时确实需要。对象复制可以通过clone关键字来完成。这种复制是通过调用对象的__clone()方法实现的,但是对象中的__clone()方法不能被直接调用。

对象比较

比较运算符==为真的条件是:两个对象的属性和属性值都相等,而且两个对象是同一个类的实例。

继承与统一个基类的两个子类的对象不会相等==

<code><?php 
class Base {}
class A extends Base {}
class B extends Base {}
$a = new A;
$b = new B;
var_dump($a == $b);  // false</code>

全等运算符===为真的条件是:两个对象变量一定要指向某个类的同一个实例(即同一个对象)。

类型约束

类型约束是指函数的参数可以指定必须为对象、接口、数组或者callable类型。但是类型约束不能用于标量类型如intstringtraits也不允许。类型约束允许NULL值。

后期静态绑定

后期静态绑定,用于在继承范围内引用静态调用的类。

转发调用,指的是通过以下几种方式进行的静态调用:self::parent::static::以及forward_static_call()

后期静态绑定的工作原理是,存储了上一个非转发调用的类名。

当进行静态方法调用时,该类名即为明确指定的那个;当进行非静态方法调用时,即为该对象所属的类。

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

<code><?php 
class A {
	public static function who() {
		echo __CLASS__;
	}
	public static function test() {
		self::who();
	}
}
class B extends A {
	public static function who() {
		echo __CLASS__;
	}
}
B::test();  // A
B::who();  // B</code>

static::关键字表示运行时最初调用的类,后期静态绑定就是这样使用。如下面程序所示,也就是说调用test()时引用的类是B而不是A

<code><?php 
class A {
	public static function who() {
		echo __CLASS__;
	}
	public static function test() {
		static::who();
	}
}
class B extends A {
	public static function who() {
		echo __CLASS__;
	}
}
B::test();  // B
B::who();  // B</code>

示例2给出的是非静态环境下使用static::

后期静态绑定的解析,会一直到取得一个完全解析了的静态调用为止。另外,如果静态调用使用parent::self::将转发调用信息。

<code><?php 
class A {
	public static function foo() {
		static::who();
	}
	public static function who() {
		echo __CLASS__;
	}
}
class B extends A {
	public static function test() {
		A::foo();
		parent::foo();
		self::foo();
	}
	public static function who() {
		echo __CLASS__;
	}
}
class C extends B {
	public static function who() {
		echo __CLASS__;
	}
}
C::test();  // ACC</code>

那么问题来了,结果为什么是这样的呢?

对象和引用

默认情况下,对象时通过引用传递的。但这种说法不完全正确,其实两个对象变量不是引用的关系,只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容。

对象序列化

所有PHP里面的值,都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数能够重新把字符串变为原来的值。

序列化一个对象,将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。为了能够unserialize()一个对象,这个对象的类必须已经定义过。在应用程序中序列化对象以便在之后使用,强烈推荐在整个应用程序都包含对象的类的定义。

(全文完)

以上就介绍了类与对象 - PHP手册笔记,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn