Class

Каждое определение класса начинается ключевым словом class, следующим за именем класса, которое может быть любым не зарезервированным в PHP именем, за которым следует пара фигурных скобок, внутри которых содержится определение членов класса и методов. Псевдо-переменная $this доступна, когда метод вызывается внутри контекста объекта. $this - это ссылка на вызывающий объект (обычно это объект, которому принадлежит метод, но который может быть другим объектом, если метод вызывается статически из контекста другого объекта). Это иллюстрируется следующим примером:

Переменная $this в объектно-ориентированном языке

<?php
class A
{
function foo()
{
if (isset($this)) {
echo '$this определена (';
echo get_class($this);
echo ")\n";
} else {
echo "\$this не определена.\n";
}
}
}

class B
{
function bar()
{
A::foo();
}
}

$a = new A();
$a->foo();
A::foo();
$b = new B();
$b->bar();
B::bar();
?>

Результат выполнения данного примера:

$this определена (a)
$this не определена.$this определена (b)
$this не определена.

Простое определение класса

<?php
class SimpleClass
{
// member declaration
public $var = 'a default value';

// method declaration
public function displayVar() {
echo $this->var;
}
}
?>

Дефолтное значение должно быть постоянным выражением, не (для примера) переменной, членом класса или вызовом функции.

Дефолтное значение члена класса

<?php
class SimpleClass
{
// неправильная декларация члена:
public $var1 = 'hello '.'world';
public $var2 = <<hello worldEOD;
public $var3 = 1+2;
public $var4 = self::myStaticMethod();
public $var5 = $myVar;

// правильное объявление члена
public $var6 = myConstant;
public $var7 = self::classConstant;
public $var8 = array(true, false);


}
?>

Абстрактные классы

PHP5 поддерживает определение абстрактных классов и методов. Создавать экземпляр класса, который был объявлен абстрактным, нельзя. Класс, в котором объявлен хотя бы один абстрактный метод, должен также быть объявлен абстрактным. Методы, объявленные как абстрактные, несут, по существу, лишь описательный смысл и не могут включать какой-либо функционал.

Пример абстрактного класса

<?php
abstract class AbstractClass {

/* Данный метод должен быть определён в дочернем классе */
abstract protected function getValue();

/* Общий метод */
public function print() {
print $this->getValue();
}

}

class ConcreteClass1 extends AbstractClass {

protected function getValue() {
return "ConcreteClass1";
}

}

class ConcreteClass2 extends AbstractClass {

protected function getValue() {
return "ConcreteClass2";
}

}

$class1 = new ConcreteClass1;
$class1->print();

$class2 = new ConcreteClass2;
$class2->print();
?>

Код, предназначенный для прежних версий PHP, должен работать без изменений, если в нём отсутствуют классы или функции, именованные "abstract".

Интерфейсы объектов

Интерфейсы объектов позволяют программисту создавать код, который указывает, какие методы и свойства должен включать класс, без необходимости описывания их функционала.

Интерфейсы объявляются так же, как и обычные классы, но с использованием ключевого слова "interface"; тела методов интерфейсов должны быть пустыми. Для включения интерфейса в класс программист должен использовать ключевое слово "implements" и описать функционал методов, перечисленных во включаемом интерфейсе. Если это требуется, классы могут включать более одного интерфейса путём их перечисления через пробел.

Если класс включает какой-либо интерфейс и не описывает функционал всех методов этого интерфейса, выполнение кода с использованием такого класса завершится фатальной ошибкой, сообщающей, какие именно методы не были описаны.

Пример интерфейса

<?php
interface ITemplate
{
public function setVariable($name, $var);
public function getHtml($template);
}

class Template implements ITemplate
{
private $vars = array();

public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}

public function getHtml($template)
{
foreach($this->vars as $name => $value) {
$template = str_replace('{'.$name.'}', $value, $template);
}

return $template;
}
}
?>

Перегрузка

Вызовы методов, как и обращения к свойствам объекта, могут быть перегружены с использованием методов __call, __get и __set. Эти методы будут срабатывать только в том случае, если объект или наследуемый объект не содержат свойства или метода, к которому осуществляется доступ.

Перегрузка свойств

void __set ( string имя, mixed значение )
void __get ( mixed имя )

С помощью этих методов обращения к свойствам класса могут быть перегружены с целью выполнения произвольного кода, описанного в классе. В аргументе имя передаётся имя свойства, к которому производится обращение. Аргумент значение метода __set() должен содержать значение, которое будет присвоено свойству класса с именем имя.

Пример перегрузки с использование __get и __set

<?php
class Setter {
public $n;
private $x = array("a" => 1, "b" => 2, "c" => 3);

function __get($nm) {
print "ЧИтаем [$nm]\n";

if (isset($this->x[$nm])) {
$r = $this->x[$nm];
print "Получили: $r\n";
return $r;
} else {
print "Ничего!\n";
}
}

function __set($nm, $val) {
print "Пишем $val в [$nm]\n";

if (isset($this->x[$nm])) {
$this->x[$nm] = $val;
print "OK!\n";
} else {
print "Всё плохо!\n";
}
}
}

$foo = new Setter();
$foo->n = 1;
$foo->a = 100;
$foo->a++;
$foo->z++;
var_dump($foo);
?>

Результатом выполнения будет:

Пишем 100 в [a]
OK!
Читаем [a]
Получили: 100
Пишем 101 в [a]
OK!
Читаем [z]
Ничего!
Пишем 1 в [z]
Всё плохо!
object(Setter)#1 (2) {
["n"]=>
int(1)
["x:private"]=>
array(3) {
["a"]=>
int(101)
["b"]=>
int(2)
["c"]=>
int(3)
}
}

Перегрузка методов

mixed __call ( string имя, array аргументы )

С использованием этого метода, методы класса могут быть перегружены с целью выполнения произвольного кода, описанного в классе. В аргументе имя передаётся имя вызванного метода. Аргументы, которые были переданы методу при обращении, будут возвращены чере аргументы. Значение, возвращённое методом __call(), будет передано вызывающему оператору.

Пример перегрузки с использованием __call

<?php
class Caller {
private $x = array(1, 2, 3);

function __call($m, $a) {
print "Вызван метод $m :\n";
var_dump($a);
return $this->x;
}
}

$foo = new Caller();
$a = $foo->test(1, "2", 3.4, true);
var_dump($a);
?>

Результатом выполнения будет:

Вызван метод test:array(4) {
[0]=>
int(1)
[1]=>
string(1) "2"
[2]=>
float(3.4)
[3]=>
bool(true)
}
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}

Итераторы объектов

PHP 5 предоставляет механизм итераторов для получения списка всех свойств какого-либо объекта, например, для использования совместно с оператором foreach. По умолчанию, в итерации будут участвовать все свойства, объявленные как public.

Итерация простого объекта

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

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

}

$class = new MyClass();

foreach($class as $key => $value) {
print "$key => $value\n";
}

Результат:

var1 => value 1
var2 => value 2
var3 => value 3

Как показывает результат, foreach проитерировал все принадлежащие объекту public-свойства. Кроме того, программист может включить (implement) в свой класс один из внутренних объектов PHP5, именуемый Iterator. Это позволит программисту самому определить, каким именно образом будет осуществляться итерация объекта.

Объект Iteration, включающий интерфейс Iterator

<?php
class MyIterator implements Iterator {

private $var = array();

public function __construct($array) {
if (is_array($array) ) {
$this->var = $array;
}
}

public function rewind() {
echo "перемотка в начало\n";
reset($this->var);
}

public function current() {
$var = current($this->var);
echo "текущий: $var\n";
return $var;
}

public function key() {
$var = key($this->var);
echo "ключ: $var\n";
return $var;
}

public function next() {
$var = next($this->var);
echo "следующий: $var\n";
return $var;
}

public function valid() {
$var = $this->current() !== false;
echo "верный: {$var}\n";
return $var;
}

}

$values = array(1,2,3);
$it = new MyIterator($values);

foreach ($it as $a => $b) {
print "$a: $b\n";
}

Результатом выполнения этого кода станет:

перемотка в началотекущий: 1
верный: 1
текущий: 1
ключ: 0
0: 1
следующий: 2
текущий: 2
верный: 1
текущий: 2
ключ: 1
1: 2
следующий: 3
текущий: 3
верный: 1
текущий: 3
ключ: 2
2: 3
следующий:текущий:верный:

Программист также может объявить класс так, чтобы ему не пришлось описывать все методы, перечисленные в интерфейсе Iterator, включая интерфейс PHP5 IteratorAggregate.

Объект Iteration, включающий интерфейс IteratorAggregate

<?php
class MyCollection implements IteratorAggregate {
private $items = array();
private $count = 0;

/* Required definition of interface IteratorAggregate */
public function getIterator() {
return new MyIterator($this->items);
}

public function add($value) {
$this->items[$this->count++] = $value;
}

}

$coll = new MyCollection();
$coll->add('value 1');
$coll->add('value 2');
$coll->add('value 3');

foreach ($coll as $key => $val) {
echo "key/value: [$key -> $val]\n\n";
}

?>

Результат:

rewindingcurrent: value 1
valid: 1
current: value 1
key: 0
key/value: [0 -> value 1]

next: value 2
current: value 2
valid: 1
current: value 2
key: 1
key/value: [1 -> value 2]

next: value 3
current: value 3
valid: 1
current: value 3
key: 2
key/value: [2 -> value 3]

next:current:valid:

Шаблоны

Шаблоны - это способ описания лучших вариантов решения задач. Они дают гибкие решения общих задач программирования.

Factory

Шаблон Factory обеспечивает возможность создания экземпляра объекта во время выполнения. Он называется Factory Pattern ("Фабричный шаблон"), поскольку он отвечает за "изготовление" объекта. Метод Factory с параметрами получает в качестве аргумента имя класса для получения экземпляра класса.

Метод Factory с параметрами

<?php
class Example
{
// Метод Factory с параметрами
public static function factory($type)
{
if (include_once 'Drivers/' . $type . '.php') {
$classname = 'Driver_' . $type;
return new $classname;
} else {
throw new Exception ('Driver not found');
}
}
}
?>

Определение этого метода в классе позволяет загружать драйверы "на лету". Если класс Example является абстрактным классом базы данных, загрузка драйверов MySQL и SQLite может быть выполнена следующим образом:

<?php 
// Load a MySQL Driver
$mysql = Example::factory('MySQL');

// Load a SQLite Driver
$sqlite = Example::factory('SQLite');
?>

Singleton

Шаблон Singleton применяется в ситуациях, когда необходим единственный экземпляр класса. Самым распространенным примером этого может служить соединение с базой данных. Применение этого шаблона позволяет программисту сделать этот одиночный экземпляр легко доступным для многих других объектов.

Функция Singleton

<?php
class Example
{
// Hold an instance of the class
private static $instance;

// private конструктор; предотвращает прямое создание объекта
private function __construct()
{
echo 'I am constructed';
}

// Метод singleton
public static function singleton()
{
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}

return self::$instance;
}

// Метод Example
public function bark()
{
echo 'Woof!';
}

// Предотвращаем клонирование экземпляра пользователем
public function __clone()
{
trigger_error('Clone is not allowed.', E_USER_ERROR);
}

}

?>

Это позволяет восстановление единственного экземпляра класса Example

<?php 
// Это приведет к ошибке, потому что конструктор имеет статус private
$test = new Example;

// Это позволит восстановить единственный экземпляр класса$test = Example::singleton();
$test->bark();

// Это выдаст ошибку E_USER_ERROR.
$test_clone = clone($test);

?>

"Волшебные" методы

Имена функций:

__construct
__destruct
__call
__get
__set
__isset
__unset
__sleep
__wakeup
__toString
__set_state
__clone and
__autoload

являются "волшебными" в классах PHP. Эти имена не могут быть присвоены любым классам без ассоциации с "волшебной" функциональностью.

Предостережение

В PHP все имена функций, начинающиеся с __, резервируются, как "волшебные". Не рекомендуется использовать имена, начинающиеся с __, для целей, отличающихся от задокументированной "волшебной" функциональности.

__sleep и __wakeup

serialize() проверяет, содержится ли в классе функция с именем __sleep. Если содержится, то эта функция выполняется перед любой из операций сериализации. Она может очистить объект и предполагает возвращение массива имен всех переменных объекта, который должен быть сериализован.

Обычным использованием __sleep является закрытие соединений с любыми базами данных объекта, завершение операций и т.п. Также функция полезна в случае работы с большими объектами, в полном сохранении которых нет необходимости.

В противоположность, unserialize() проверяет присутствие функции с волшебным именем __wakeup. Если такая функция присутствует, она может реконструировать ресурсы, которые может иметь объект.

Надлежащим использованием __wakeup является восстановление соединений с базой данных, которые могли быть потеряны в процессе сериализации, и выполнение других задач реинициализации.

Sleep и wakeup

<?php
class Connection {
protected $link;
private $server, $username, $password, $db;

public function __construct($server, $username, $password, $db)
{
$this->server = $server;
$this->username = $username;
$this->password = $password;
$this->db = $db;
$this->connect();
}

private function connect()
{
$this->link = mysql_connect($this->server, $this->username, $this->password);
mysql_select_db($this->db, $this->link);
}

public function __sleep()
{
mysql_close($this->link);
}

public function __wakeup()
{
$this->connect();
}
}
?>

__toString

Метод __toString позволяет классу решить, как он будет реагировать при преобразовании в строку.

Простой пример

<?php  
// Объявляем простой класс
class TestClass
{
public $foo;

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

public function __toString() {
return $this->foo;
}
}

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

Результат выполнения данного примера:

Hello

Не имеет значения, что до PHP 5.2.0 метод __toString вызывался только тогда, когда он был прямо объединен с echo() или print().

__set_state

Этот статический метод вызывается для классов, экспортированных функцией var_export(), начиная с PHP 5.1.0.

Единственным параметром этого метода является массив, содержащий экспортированные свойства в форме массива array('property' => value, ...).

Final

Ключевое слово "final"

Разместив перед объявлениями методов или свойств класса ключевое слово "final", вы можете предотвратить их переопределение в дочерних классах.

Пример окончательных (final) методов

<?php
class BaseClass {
public function test() {
echo "Вызван метод BaseClass::test()\n";
}

final public function moreTesting() {
echo "Вызван метод BaseClass::moreTesting()\n";
}
}

class ChildClass extends BaseClass {
public function moreTesting() {
echo "Вызван метод ChildClass::moreTesting()\n";
}
}
// Выполнение заканчивается фатальной ошибкой: Cannot override final method BaseClass::moreTesting()
// (Метод BaseClass::moretesting() не может быть переопределён)
?>

Клонирование объектов

Создание копии объекта с абсолютно идентичными свойствами не всегда является приемлемым вариантом. Хорошим примером необходимости копирования конструкторов может послужить ситуация, когда у вас есть объект, представляющий собой окно GTK и содержащий ресурс-идентификатор этого окна; когда вы создаете копию этого объекта, вам может понадобиться, чтобы копия объекта содержала ресурс-идентификатор нового окна. Другим примером может послужить ситуация, когда ваш объект содержит ссылку на какой-либо другой используемый объект и, когда вы создаёте копию ссылающегося объекта, вам нужно также создать новый экземпляр содержащегося объекта, так, чтобы копия объекта содержала собственный отдельный экземпляр содержащегося объекта.

Копия объекта создается с использованием вызова clone (который вызывает метод __clone() объекта, если это возможно). Вызов метода __clone() не может быть осуществлён непосредственно

$copy_of_object = clone $object;

Когда программист запрашивает создание копии объекта, PHP5 определит, был ли для этого объекта объявлен метод __clone() или нет. Если нет, будет вызван метод __clone(), объявленный по умолчанию, который скопирует все свойства объекта. Если метод __clone() был объявлен, создание копий свойств в копии объекта полностью возлагается на него. Для удобства, движок обеспечивает программиста функцией, которая импортирует все свойства из объекта-источника, так что программист может осуществить позначное копирование свойств и переопределять только необходимые.

Клонирование объекта

<?php
class MyCloneable {
static $id = 0;

function MyCloneable() {
$this->id = self::$id++;
}

function __clone() {
$this->address = "Москва";
$this->id = self::$id++;
}
}

$obj = new MyCloneable();

$obj->name = "Привет";
$obj->address = "Самара";

print $obj->id . "\n";

$obj_cloned = clone $obj;

print $obj_cloned->id . "\n";
print $obj_cloned->name . "\n";
print $obj_cloned->address . "\n";
?>

Сравнение объектов

В PHP5 сравнение объектов является более сложным процессом, чем в PHP4, а также процессом, более соответствующим идеологии объектно-ориентированного языка (здесь не имеется в виду, что PHP 5 является таковым).

При использовании оператора сравнения (==), свойства объектов просто сравниваются друг с другом, а именно: два объекта равны, если они содержат одинаковые свойства и одинаковые их значения и являются экземплярами одного и того же класса.

С другой стороны, при использовании оператора идентичности (===), свойства объекта считаются идентичными тогда и только тогда, когда они ссылаются на один и тот же экземпляр одного и того же класса.

Следующий пример внесёт ясность.

Пример сравнения объектов в PHP5

<?php
function bool2str($bool) {
if ($bool === false) {
return 'FALSE';
} else {
return 'TRUE';
}
}

function compareObjects(&$o1, &$o2) {
echo 'o1 == o2 : '.bool2str($o1 == $o2)."\n";
echo 'o1 != o2 : '.bool2str($o1 != $o2)."\n";
echo 'o1 === o2 : '.bool2str($o1 === $o2)."\n";
echo 'o1 !== o2 : '.bool2str($o1 !== $o2)."\n";
}

class Flag {
var $flag;

function Flag($flag=true) {
$this->flag = $flag;
}
}

class OtherFlag {
var $flag;

function OtherFlag($flag=true) {
$this->flag = $flag;
}
}

$o = new Flag();
$p = new Flag();
$q = $o;
$r = new OtherFlag();

echo "Два экземпляра одного и того же класса\n";
compareObjects($o, $p);

echo "\nДве ссылки на один и тот же экземпляр\n";
compareObjects($o, $q);

echo "\nЭкземпляры двух разных классов\n";
compareObjects($o, $r);
?>

Результатом выполнения этого кода будет:

Два экземпляра одного и того же класса
o1 == o2 : TRUE
o1 != o2 : FALSE
o1 === o2 : FALSE
o1 !== o2 : TRUE

Две ссылки на один и тот же экземпляр
o1 == o2 : TRUE
o1 != o2 : FALSE
o1 === o2 : TRUE
o1 !== o2 : FALSE

Экземпляры двух разных классов
o1 == o2 : FALSE
o1 != o2 : TRUE
o1 === o2 : FALSE
o1 !== o2 : TRUE

Отражение

PHP 5 содержит комплект API отражений, который дает возможность восстанавливать исходную информацию о классах, интерфейсах, функциях и методах равно как и расширения. Кроме того, API отражений также позволяет восстанавливать комментарии документации к функциям, классам и методам.

API отражения является объектно-ориентированным расширением Zend Engine, состоящим из следующих классов:

<?php
class Reflection { }
interface Reflector { }
class ReflectionException extends Exception { }
class ReflectionFunction implements Reflector { }
class ReflectionParameter implements Reflector { }
class ReflectionMethod extends ReflectionFunction { }
class ReflectionClass implements Reflector { }
class ReflectionObject extends ReflectionClass { }
class ReflectionProperty implements Reflector { }
class ReflectionExtension implements Reflector { }
?>

Пример. Стандартное применение API отражения

<?php
Reflection::export(new ReflectionClass('Exception'));
?>

Результат выполнения данного примера:

Class [  class Exception ] {
- Constants [0] {
}

- Static properties [0] {
}

- Static methods [0] {
}

- Properties [6] {
Property [ protected $message ]
Property [ private $string ]
Property [ protected $code ]
Property [ protected $file ]
Property [ protected $line ]
Property [ private $trace ]
}

- Methods [9] {
Method [ final private method __clone ] {
}

Method [ public method __construct ] {

- Parameters [2] {
Parameter #0 [ $message ]
Parameter #1 [ $code ]
}
}

Method [ final public method getMessage ] {
}

Method [ final public method getCode ] {
}

Method [ final public method getFile ] {
}

Method [ final public method getLine ] {
}

Method [ final public method getTrace ] {
}

Method [ final public method getTraceAsString ] {
}

Method [ public method __toString ] {
}
}
}

ReflectionException

ReflectionException расширяет стандартный класс Exception и генерируется API отражений. Не содержит каких-либо специфических методов или свойств.

ReflectionFunction

Класс ReflectionFunction позволяет восстанавливать исходную информацию о функции

<?php
class ReflectionFunction implements Reflector
{
final private __clone()
public object __construct(string name)
public string __toString()
public static string export(string name, bool return)
public string getName()
public bool isInternal()
public bool isUserDefined()
public string getFileName()
public int getStartLine()
public int getEndLine()
public string getDocComment()
public array getStaticVariables()
public mixed invoke(mixed args)
public mixed invokeArgs(array args)
public bool returnsReference()
public ReflectionParameter[] getParameters()
public int getNumberOfParameters()
public int getNumberOfRequiredParameters()
}
?>

Замечание: getNumberOfParameters() И getNumberOfRequiredParameters() были добавлены в PHP 5.0.3, а invokeArgs() - в PHP 5.1.0.

Для разбора функции сначала придется создать экземпляр класса ReflectionFunction. Затем станет возможным вызов любого из вышеприведенных методов этого экземпляра.

Пример. Использование класса ReflectionFunction

<?php
/**
* Простой счетчик
*
* @return int
*/
function counter()
{
static $c = 0;
return $c++;
}

// Создаем экземпляр класса Reflection_Function
$func = new ReflectionFunction('counter');

// Выводим основную информациюprintf(
"===> The %s function '%s'\n".
" declared in %s\n".
" lines %d to %d\n",
$func->isInternal() ? 'internal' : 'user-defined',
$func->getName(),
$func->getFileName(),
$func->getStartLine(),
$func->getEndline()
);

// Выводим комментарии документации
printf("---> Documentation:\n %s\n", var_export($func->getDocComment(), 1));

// Выводим статические переменные при их наличии
if ($statics = $func->getStaticVariables())
{
printf("---> Static variables: %s\n", var_export($statics, 1));
}

// Вызываем функциюprintf("---> Invokation results in: ");
var_dump($func->invoke());

// можем предпочесть метод export()
echo "\nReflectionFunction::export() results:\n";
echo ReflectionFunction::export('counter');
?>

Замечание: метод invoke() принимает переменное число аргументов, которые передаются функцииon таким же образом, как и в call_user_func().

ReflectionParameter

Класс ReflectionParameter восстанавливает информацию о параметрах функций и методов

<?php
class ReflectionParameter implements Reflector
{
final private __clone()
public object __construct(string name)
public string __toString()
public static string export(mixed function, mixed parameter, bool return)
public string getName()
public bool isPassedByReference()
public ReflectionFunction getDeclaringFunction()
public ReflectionClass getDeclaringClass()
public ReflectionClass getClass()
public bool isArray()
public bool allowsNull()
public bool isPassedByReference()
public bool getPosition()
public bool isOptional()
public bool isDefaultValueAvailable()
public mixed getDefaultValue()
}
?>

Замечание: getDefaultValue(), isDefaultValueAvailable() и isOptional() были добавлены в PHP 5.0.3, а isArray() - в PHP 5.1.0. getDeclaringFunction() and getPosition() были добавлены в PHP 5.1.3.

Для разбора параметров функции сначала создается экземпляр класса ReflectionFunction или ReflectionMethod, а затем используются их метод getParameters() для получения массива параметров.

Пример. Использование класса ReflectionParameters

<?php
function foo($a, $b, $c) { }
function bar(Exception $a, &$b, $c) { }
function baz(ReflectionFunction $a, $b = 1, $c = null) { }
function abc() { }

// Создаем экземпляр класса Reflection_Function с
// параметром, заданным из коммандной строки $reflect = new ReflectionFunction($argv[1]);

echo $reflect;

foreach ($reflect->getParameters() as $i => $param) {
printf(
"-- Parameter #%d: %s {\n".
" Class: %s\n".
" Allows NULL: %s\n".
" Passed to by reference: %s\n".
" Is optional?: %s\n".
"}\n",
$i,
$param->getName(),
var_export($param->getClass(), 1),
var_export($param->allowsNull(), 1),
var_export($param->isPassedByReference(), 1),
$param->isOptional() ? 'yes' : 'no'
);
}
?>

ReflectionClass

Класс ReflectionClass позволяет восстанавливать исходную информацию классов

<?php
class ReflectionClass implements Reflector
{
final private __clone()
public object __construct(string name)
public string __toString()
public static string export(mixed class, bool return)
public string getName()
public bool isInternal()
public bool isUserDefined()
public bool isInstantiable()
public bool hasConstant(string name)
public bool hasMethod(string name)
public bool hasProperty(string name)
public string getFileName()
public int getStartLine()
public int getEndLine()
public string getDocComment()
public ReflectionMethod getConstructor()
public ReflectionMethod getMethod(string name)
public ReflectionMethod[] getMethods()
public ReflectionProperty getProperty(string name)
public ReflectionProperty[] getProperties()
public array getConstants()
public mixed getConstant(string name)
public ReflectionClass[] getInterfaces()
public bool isInterface()
public bool isAbstract()
public bool isFinal()
public int getModifiers()
public bool isInstance(stdclass object)
public stdclass newInstance(mixed args)
public stdclass newInstanceArgs(array args)
public ReflectionClass getParentClass()
public bool isSubclassOf(ReflectionClass class)
public array getStaticProperties()
public mixed getStaticPropertyValue(string name [, mixed default])
public void setStaticPropertyValue(string name, mixed value)
public array getDefaultProperties()
public bool isIterateable()
public bool implementsInterface(string name)
public ReflectionExtension getExtension()
public string getExtensionName()
}
?>

Замечание: hasConstant(), hasMethod(), hasProperty(), getStaticPropertyValue() и setStaticPropertyValue() были введены в PHP 5.1.0, а newInstanceArgs() - в PHP 5.1.3.

Для восстановления информации класса сперва создается экземпляр класса ReflectionClass. Затем можно вызывать любой из вышеприведенных методов этого экземпляра.

Пример. Использование класса ReflectionClass

<?php
interface Serializable
{
// ...
}

class Object
{
// ...
}

/**
* class счетчика
*/
class Counter extends Object implements Serializable
{
const START = 0;
private static $c = Counter::START;

/**
* Вызываем счетчик
*
* @access public
* @return int
*/
public function count() {
return self::$c++;
}
}

// Создаем экземпляр класса ReflectionClass$class = new ReflectionClass('Counter');

// Выводим базовую информациюprintf(
"===> The %s%s%s %s '%s' [extends %s]\n" .
" declared in %s\n" .
" lines %d to %d\n" .
" having the modifiers %d [%s]\n",
$class->isInternal() ? 'internal' : 'user-defined',
$class->isAbstract() ? ' abstract' : '',
$class->isFinal() ? ' final' : '',
$class->isInterface() ? 'interface' : 'class',
$class->getName(),
var_export($class->getParentClass(), 1),
$class->getFileName(),
$class->getStartLine(),
$class->getEndline(),
$class->getModifiers(),
implode(' ', Reflection::getModifierNames($class->getModifiers()))
);

// Выводим комментарии документации
printf("---> Documentation:\n %s\n", var_export($class->getDocComment(), 1));

// Какие интерфейсы задействованы классом
printf("---> Implements:\n %s\n", var_export($class->getInterfaces(), 1));

// Константы класса
printf("---> Constants: %s\n", var_export($class->getConstants(), 1));

// Свойства класса
printf("---> Properties: %s\n", var_export($class->getProperties(), 1));

// Методы
printf("---> Methods: %s\n", var_export($class->getMethods(), 1));

// Если класс позволяет, создаем его экземпляр
if ($class->isInstantiable()) {
$counter = $class->newInstance();

echo '---> $counter is instance? ';
echo $class->isInstance($counter) ? 'yes' : 'no';

echo "\n---> new Object() is instance? ";
echo $class->isInstance(new Object()) ? 'yes' : 'no';
}
?>

Замечание: метод newInstance() принимает переменное число аргументов, которые передаются функции так же, как и в call_user_func().

Замечание: выражения $class = new ReflectionClass('Foo'); $class->isInstance($arg) эквивалентны $arg instanceof Foo или is_a($arg, 'Foo').

ReflectionObject

Класс ReflectionObject позволяет восстанавливать исходную информацию объектов

<?php
class ReflectionObject extends ReflectionClass
{
final private __clone()
public object __construct(mixed object)
public string __toString()
public static string export(mixed object, bool return)
}
?>

ReflectionMethod

Класс ReflectionMethod позволяет восстанавливать исходную информацию методов класса

<?php
class ReflectionMethod extends ReflectionFunction
{
public __construct(mixed class, string name)
public string __toString()
public static string export(mixed class, string name, bool return)
public mixed invoke(stdclass object, mixed args)
public mixed invokeArgs(stdclass object, array args)
public bool isFinal()
public bool isAbstract()
public bool isPublic()
public bool isPrivate()
public bool isProtected()
public bool isStatic()
public bool isConstructor()
public bool isDestructor()
public int getModifiers()
public ReflectionClass getDeclaringClass()

// Inherited from ReflectionFunction
final private __clone()
public string getName()
public bool isInternal()
public bool isUserDefined()
public string getFileName()
public int getStartLine()
public int getEndLine()
public string getDocComment()
public array getStaticVariables()
public bool returnsReference()
public ReflectionParameter[] getParameters()
public int getNumberOfParameters()
public int getNumberOfRequiredParameters()
}
?>

Для исследования метода сначала создается экземпляр класса ReflectionMethods. Затем можно вызывать любой из вышеприведенных методов этого экземпляра.

Пример. Использование класса ReflectionMethod

<?php
class Counter
{
private static $c = 0;

/**
* Инкрементный счетчик
*
* @final
* @static
* @access public
* @return int
*/
final public static function increment()
{
return ++self::$c;
}
}

// Создаем экземпляр класса Reflection_Method
$method = new ReflectionMethod('Counter', 'increment');

// Выводим основную информацию
printf(
"===> The %s%s%s%s%s%s%s method '%s' (which is %s)\n" .
" declared in %s\n" .
" lines %d to %d\n" .
" having the modifiers %d[%s]\n",
$method->isInternal() ? 'internal' : 'user-defined',
$method->isAbstract() ? ' abstract' : '',
$method->isFinal() ? ' final' : '',
$method->isPublic() ? ' public' : '',
$method->isPrivate() ? ' private' : '',
$method->isProtected() ? ' protected' : '',
$method->isStatic() ? ' static' : '',
$method->getName(),
$method->isConstructor() ? 'the constructor' : 'a regular method',
$method->getFileName(),
$method->getStartLine(),
$method->getEndline(),
$method->getModifiers(),
implode(' ', Reflection::getModifierNames($method->getModifiers()))
);

// Комментарии
printf("---> Documentation:\n %s\n", var_export($method->getDocComment(), 1));

// Статические переменные при их наличии
if ($statics= $method->getStaticVariables()) {
printf("---> Static variables: %s\n", var_export($statics, 1));
}

// Вызов метода
printf("---> Invokation results in: ");
var_dump($method->invoke(NULL));
?>

Замечание: При попытке вызова частного (private), защищенного (protected) или абстрактного (abstract) метода результатом будет исключение, генерируемое методом invoke().

Замечание: для статичных (static) методов, как видно выше, следует передавать NULL в качестве первого аргумента invoke(). Не статичным методам передаются экземпляры класса.

ReflectionProperty

Класс ReflectionProperty позволяет восстанавливать исходную информацию свойств класса.

<?php
class ReflectionProperty implements Reflector
{
final private __clone()
public __construct(mixed class, string name)
public string __toString()
public static string export(mixed class, string name, bool return)
public string getName()
public bool isPublic()
public bool isPrivate()
public bool isProtected()
public bool isStatic()
public bool isDefault()
public int getModifiers()
public mixed getValue(stdclass object)
public void setValue(stdclass object, mixed value)
public ReflectionClass getDeclaringClass()
public string getDocComment()
}
?>

Замечание: getDocComment() был добавлен в PHP 5.1.0.

Для получения информации свойства сначала создается экземпляр класса ReflectionProperty. Затем становится возможным вызов любого из вышеприведенных методов этого экземпляра.

Пример. Применение класса ReflectionProperty

<?php
class String
{
public $length = 5;
}

// Создание экземпляра класса ReflectionProperty
$prop = new ReflectionProperty('String', 'length');

// Вывод базовой информации
printf(
"===> The%s%s%s%s property '%s' (which was %s)\n" .
" having the modifiers %s\n",
$prop->isPublic() ? ' public' : '',
$prop->isPrivate() ? ' private' : '',
$prop->isProtected() ? ' protected' : '',
$prop->isStatic() ? ' static' : '',
$prop->getName(),
$prop->isDefault() ? 'declared at compile-time' : 'created at run-time',
var_export(Reflection::getModifierNames($prop->getModifiers()), 1)
);

// Создание экземпляра класса String
$obj= new String();

// Получение текущего значения
printf("---> Value is: ");
var_dump($prop->getValue($obj));

// Изменение значения
$prop->setValue($obj, 10);
printf("---> Setting value to 10, new value is: ");
var_dump($prop->getValue($obj));

// Дамп объекта
var_dump($obj);
?>

Замечание: Попытка получить или задать значение свойства класса типовы private или protected приведет к генерации исключения.

ReflectionExtension

Класс ReflectionExtension позволяет восстанавливать исходную информацию расширений. Вы можете восстановить все загруженные расширения во время исполнения сценария используя get_loaded_extensions()

<?php
class ReflectionExtension implements Reflector {
final private __clone()
public __construct(string name)
public string __toString()
public static string export(string name, bool return)
public string getName()
public string getVersion()
public ReflectionFunction[] getFunctions()
public array getConstants()
public array getINIEntries()
public ReflectionClass[] getClasses()
public array getClassNames()
}
?>

Для получения информации расширения сначала вызывается экземпляр класса ReflectionExtension. Затем становится возможным вызов любого из вышеперечисленных методов этого экземпляра.

Пример. Использование класса ReflectionExtensions

<?php 
// Create an instance of the ReflectionProperty class
$ext = new ReflectionExtension('standard');

// Print out basic information
printf(
"Name : %s\n" .
"Version : %s\n" .
"Functions : [%d] %s\n" .
"Constants : [%d] %s\n" .
"INI entries : [%d] %s\n" .
"Classes : [%d] %s\n",
$ext->getName(),
$ext->getVersion() ? $ext->getVersion() : 'NO_VERSION',
sizeof($ext->getFunctions()),
var_export($ext->getFunctions(), 1),

sizeof($ext->getConstants()),
var_export($ext->getConstants(), 1),

sizeof($ext->getINIEntries()),
var_export($ext->getINIEntries(), 1),

sizeof($ext->getClassNames()),
var_export($ext->getClassNames(), 1)
);
?>

Расширение классов отражения

В случае, если необходимо создавать специализированные версии встроенных классов (скажем, для создания цветной версии HTML во время экспорта, для получения легко доступных переменных вместо методов или для получения методов утилит), имеется возможность расширить встроенные классы.

Пример. Расширение встроенных классов

<?php
/**
* My Reflection_Method class
*/
class My_Reflection_Method extends ReflectionMethod
{
public $visibility = '';

public function __construct($o, $m)
{
parent::__construct($o, $m);
$this->visibility= Reflection::getModifierNames($this->getModifiers());
}
}

/**
* Demo class #1
*
*/
class T {
protected function x() {}
}

/**
* Demo class #2
*
*/
class U extends T {
function x() {}
}

// Вывод информации
var_dump(new My_Reflection_Method('U', 'x'));
?>

Замечание: Если вы переписываете конструктор, не забывайте вызывать родительский конструктор _before_ перед любым кодом, который вы вставляете.

Несоблюдение этого условия вызовет ошибку: Fatal error: Internal error: Failed to retrieve the reflection object

Extends

Класс может наследовать методы и члены другого класса при использовании ключевого слова extends в объявлении. Невозможно расширить множество классов, класс может наследовать только один базовый класс.

Унаследованные методы и члены могут быть переопределены путем переобъявления их под тем же именем, определенным в базовом классе. К переопределенному методу или члену можно получить доступ, ссылаясь на него с помощью parent::

Простое наследование класса

<?php
class ExtendClass extends SimpleClass
{
// Переопределим родительский метод
function displayVar()
{
echo "Расширяемый класс\n";
parent::displayVar();
}
}

$extended = new ExtendClass();
$extended->displayVar();
?>

Результат выполнения данного примера:

Расширяемый класс дефолтное значение

Указание типов (Type Hints)

В PHP 5 появилась новая возможность, обозначенная как Type Hinting, что можно перевести как "указание типов".

Теперь при определении функции можно указать тип ее параметра, как объект (путем указания имени класса в определении функции) или массив (начиная с PHP 5.1).

Пример. Пример Type Hinting

<?php  
// Пример класса
class MyClass
{
/**
* Тестовая функция
*
* Указываем, что первый параметр должен быть объектом типа OtherClass
*/
public function test(OtherClass $otherclass) {
echo $otherclass->var;
}


/**
* Другая тестовая функция
*
* Указываем, что первый параметр должен быть массивом
*/
public function test_array(array $input_array) {
print_r($input_array);
}
}

// Другой пример класса
class OtherClass {
public $var = 'Hello World';
}
?>

Теперь несоответствие указанному типу приводит к фатальной ошибке

<?php
// Экземпляр каждго класса
$myclass = new MyClass;
$otherclass = new OtherClass;

// Fatal Error: Первый аргумент должен быть объектом класса OtherClass
$myclass->test('hello');

// Fatal Error: Первый аргумент должен быть экземпляром OtherClass
$foo = new stdClass;
$myclass->test($foo);

// Fatal Error: Первый аргумент не должен быть null$myclass->test(null);

// Работает: выводит "Hello World"$myclass->test($otherclass);

// Fatal Error: Аргумент 1 должен быть массивом
$myclass->test_array('a string');

// работает: выводит массив$myclass->test_array(array('a', 'b', 'c'));
?>

Указание типов работает и с функциями:

<?php  
// Пример класса
class MyClass {
public $var = 'Hello World';
}

/**
* Тестовая функция
*
* Первый параметр должен быть объектом типа MyClass
*/
function MyFunction (MyClass $foo) {
echo $foo->var;
}

// Работает$myclass = new MyClass;
MyFunction($myclass);
?>

Указание типов может работать только с типами object или array (начиная с PHP 5.1). Указание типов int и string не поддерживается.

New

Для создания экземпляра класса должен быть создан новый объект и он должен быть присвоен переменной. Объект всегда будет присваиваться переменной при создании, кроме случаев, когда объект имеет конструктор, определенный для генерации ошибки. Классы должны быть определены до создания их экземпляров (а в некоторых случаях это является требованием).

Создание экземпляра класса

<?php
$instance = new SimpleClass();
?>

При присвоении уже существующего экземпляра класса новой переменной, новая переменная получит доступ к этому же экземпляру, как к объекту, который был присвоен. Это такое же поведение, как и при передаче экземпляра класса функции. Копия уже созданного объекта может быть получена путем его клонирования.

Присвоение объекта

<?php
$assigned = $instance;
$reference =& $instance;

$instance->var = '$assigned получит это значение';

$instance = null; // $instance и $reference станут null
var_dump($instance);
var_dump($reference);
var_dump($assigned);
?>

Результат выполнения данного примера:

NULL
NULL
object(SimpleClass)#1 (1) {
["var"]=>
string(30) "$assigned получит это значение"
}

Автоматически загружающиеся объекты

Многие разработчики объектно-ориентированных приложений создают каждое исходное определение класса в отдельном файле. Наибольшую досаду вызывает то, что приходится писать длинный список необходимых включений в начале каждого скрипта (по одному на каждый класс).

В PHP5 это больше не является необходимостью. Можно определить функцию __autoload, которая автоматически вызывается при попытке использования класса, который еще не определен. При вызове такой функции движку скрипта дается последний шанс загрузить класс перед тем, как скрипт должен будет аварийно завершиться с генерацией ошибки.

Замечание: исключения, генерируемые в функции __autoload, не могут быть перехвачены в блоке перехвата, и генерируют фатальную ошибку.

Пример автозагрузки.

Этот пример пытается загрузить классы MyClass1 и MyClass2 из файлов MyClass1.php и MyClass2.php соответственно

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

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

Конструкторы и деструкторы

Конструктор

PHP 5 позволяет объявлять методы-конструкторы. Классы, в которых объявлен метод-констуктор, будут вызывать этот метод при каждом создании нового объекта, так что это может оказаться полезным, чтобы, например, инициализировать какое-либо состояние объекта перед его использованием.

Замечание: Конструкторы в классах-родителях не вызываются автоматически. Чтобы вызвать конструктор, объявленный в родительском классе, следует обратиться к методу parent::__construct().

Пример. Использование унифицированных конструкторов

<?php
class BaseClass {
function __construct() {
print "Конструктор класса BaseClass\n";
}
}

class SubClass extends BaseClass {
function __construct() {
parent::__construct();
print "Конструктор класса SubClass\n";
}
}

$obj = new BaseClass();
$obj = new SubClass();
?>

Если PHP 5 не может обнаружить объявленный метод __construct(), вызов конструктора произойдет по прежней схеме, через обращение к методу, имя которого соответствует имени класса. Может возникнуть только одна проблема совместимости старого кода, если в нём присутствуют классы с методами __construct().

Деструкторы

PHP 5 предоставляет концепцию деструкторов, сходную с теми, что применяются в других ОО языках, таких, как Java: когда освобождается последняя ссылка на объект, перед высвобождением памяти, занимаемой этим объектом, вызывается метод __destruct(), не принимающий параметров.

Пример. Пример использования деструктора

<?php
class MyDestructableClass {
function __construct() {
print "Конструктор\n";
$this->name = "MyDestructableClass";
}

function __destruct() {
print "Уничтожается " . $this->name . "\n";
}
}

$obj = new MyDestructableClass();
?>

Как и в случае с конструкторами, деструкторы, объявленные в родительском классе, не будут вызваны автоматически. Для вызова деструктора, объявленном в классе-родителе, следует обратиться к методу parent::__destruct().

Видимость

Видимость свойства или метода может быть определена путем префиксирования объявления ключевыми словами public, protected или private. К элементам, объявленным public, доступ возможен отовсюду. Protected ограничивает доступ до унаследованных и родительских классов (и до класса, который определяет элемент). Private ограничивает видимость только границами класса, в котором элемент определен.

Члены класса должны быть определены с ключевыми словами public, private или protected.

Объявление члена

<?php
/**
* Define MyClass
*/
class MyClass
{
public $public = 'Public';
protected $protected = 'Protected';
private $private = 'Private';

function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj = new MyClass();
echo $obj->public; // Работает
echo $obj->protected; // Фатальная ошибка
echo $obj->private; // Fatal Error
$obj->printHello(); // Показывает Public, Protected and Private


/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// Мы можем переобъявить public или protected метод, но не private
protected $protected = 'Protected2';

function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}

$obj2 = new MyClass2();
echo $obj->public; // Работает
echo $obj2->private; // Undefined
echo $obj2->protected; // Fatal Error
$obj2->printHello(); // Показывает Public, Protected2 или Private
?>

Замечание: Способ объявления переменной в PHP4 с помощью ключевого слова var все еще поддерживается по причинам совместимости (как синоним ключевого слова public). В PHP5 до 5.1.3, ключевое слово var будет генерировать предупреждение E_STRICT.

Видимость метода.

Методы класса должны быть определены с помощью public, private или protected. Методы без любого из этих ключевых слов определяются, как public.

Определение метода

<?php
/**
* Define MyClass
*/
class MyClass
{
// Contructors must be public
public function __construct() { }

// Declare a public method
public function MyPublic() { }

// Declare a protected method
protected function MyProtected() { }

// Declare a private method
private function MyPrivate() { }

// This is public
function Foo()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate();
}
}

$myclass = new MyClass;$myclass->MyPublic(); // Работает
$myclass->MyProtected(); // Fatal Error
$myclass->MyPrivate(); // Fatal Error
$myclass->Foo(); // Public, Protected и Private работают

/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// This is public
function Foo2()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate(); // Fatal Error
}
}

$myclass2 = new MyClass2;
$myclass2->MyPublic(); // Работает
$myclass2->Foo2(); // Public и Protected работает, Private не работает
?>

Оператор ::

"Paamayim Nekudotayim" или просто "двойное двоеточие". Используя эту лексему, программист может обращаться к константам, статическим или перегруженным свойствам или методам класса.

При обращении к этим элементам извне класса, программист должен использовать имя этого класса.

"Paamayim Nekudotayim" на первый взгляд может показаться странным словосочетанием для обозначения двойного двоеточия. Однако, во время создания Zend Engine версии 0.5 (который входил в PHP3), Andi и Zeev выбрали именно это обозначение. "Paamayim Nekudotayim" действительно значит "двойное двоеточие". На иврите. Просто это обозначение не менялось ни разу в течение всего времени разработки PHP.

Использование :: вне объявления класса

<?php
class MyClass {
const CONST_VALUE = 'Значение константы';
}
echo MyClass::CONST_VALUE;
?>

Для обращения к свойствам и методам в объявлении класса используются ключевые слова self и parent.

Использование :: в объявлении класса

<?php
class OtherClass extends MyClass {
public static $my_static = 'статическая переменная';

public static function doubleColon() {
echo parent::CONST_VALUE . "\n";
echo self::$my_static . "\n";
}
}

OtherClass::doubleColon();
?>

Когда дочерний класс перегружает методы, объявленные в классе-родителе, PHP не будет осуществлять автоматический вызов методов, принадлежащих классу-родителю. Этот функционал возлагается на метод, перегружаемый в дочернем классе. Данное правило распространяется на конструкторы и деструкторы, перегруженные и "волшебные" методы.

Обращение к методу в родительском классе

<?php
class MyClass {

protected function myFunc() {
echo "MyClass::myFunc()\n";
}
}

class OtherClass extends MyClass {

/* Override parent's definition */
public function myFunc() {

/* But still call the parent function */
parent::myFunc();
echo "OtherClass::myFunc()\n";
}
}

$class = new OtherClass();
$class->myFunc();
?>

Static

Объявление членов или методов класса static делает их доступными без создания экземпляра класса. Член, объявленный static, не может быть доступен из объекта - экземпляра класса (хотя статический метод может).

Объявление static должно следовать после объявления типа видимости. Для совместимости с PHP4, при отсутствии объявления типа видимости, члены и методы работают, как public.

Поскольку методы static возможно вызывать без создания экземпляров объектов, псевдо-переменная $this недоступна внутри метода, объявленного как static.

Фактически вызовы статических методов совершаются во время компиляции. При использовании определенного имени класса метод уже полностью идентифицирован и не применяет правил наследования. Если вызов осуществляется посредством self, то self передается в текущий класс, то есть класс, которому принадлежит код. Здесь правила наследования также не применимы.

Доступ к статическим свойствам в объекте не может быть получен оператором ->.

Вызывающие не статические методы статически генерируют предупреждение уровня E_STRICT.

Пример статического члена

<?php
class Foo
{
public static $my_static = 'foo';

public function staticValue() {
return self::$my_static;
}
}

class Bar extends Foo
{
public function fooStatic() {
return parent::$my_static;
}
}

print Foo::$my_static . "\n";

$foo = new Foo();
print $foo->staticValue() . "\n";
print $foo->my_static . "\n"; // Не определенное "Property" my_static

// $foo::my_static невозможно
print Bar::$my_static . "\n";
$bar = new Bar();
print $bar->fooStatic() . "\n";
?>

Пример метода Static

<?php
class Foo {
public static function aStaticMethod() {
// ...
}
}

Foo::aStaticMethod();
?>

Константы в объектах

Константы также могут быть объявлены и в пределах одного класса. Отличие переменных и констант состоит в том, что при объявлении последних или при обращении к ним не используется символ $. Как и Разд. Static Keyword свойства и методы, значения констант, объявленных внутри класса, не могут быть получены через переменную, содержащую экземпляр этого класса.

Объявление и использование константы

<?php
class MyClass {
const constant = 'значение константы';

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

echo MyClass::constant . "\n";

$class = new MyClass();
$class->showConstant();
/* echo $class::constant; - такое использование недопустимо */
?>