PHP-4 Эффективная работа


Определение класса
Наследование свойств: оператор extends
Конструкторы
Доступ к методам предков
Сериализация объектов
Ссылки в конструкторе

7.4. Доступ к методам предков

7.4.1. Доступ к перекрытым методам предков

Иногда возникает необходимость обратиться к функциям и переменным в классах-предках или вызвать, функции классов, для которых еще не созданы экземпляры. Для этой цели в РНР 4 предусмотрен специальный оператор — : :,. Вот пример его использования:

class A {
          function exampleO {
               echo "Я функция A::example().<br>\n";
          }
}
class В extends A {
          function exampleO {
               echo "А я переопределенная функция В::example().<br>\n";
               A::example();
          }
}
// Объект класса А не существует.
A::exampleO;

// Создаем объект класса В.
$b = new В;
$b->example();

В этом примере производится вызов функции example() класса А без создания экземпляра класса, поэтому мы не можем использовать синтаксис вида $а - > example(). Вместо этого мы вызываем example() как функцию класса, которая принадлежит самому классу, а не какому-либо из его экземпляров. Результат выполнения этой программы приведен на рис. 7.2.

Рис. 7.2. РНР позволяет получить доступ к перекрытым методам без особых проблем

Следует иметь в виду, что в РНР могут существовать функции класса, но не могут существовать переменные класса, как это принято в Java или C++, А поскольку при отсутствии экземпляра класса переменные, объявленные в его определении,просто-напросто не существуют, функции класса не должны использовать ни одну из них, хотя вполне могут пользоваться локальными и глобальными переменными.

Но вернемся к рассматриваемому нами примеру. В определении класса В производится перекрытие функции example (). Первоначальное определение функции затеняется и становится недоступным, если только вы Не укажете явным образом, что пытаетесь обратиться именно к реализации example() класса А. Для этого используется запись A: :example(). (См. также использование синтаксиса parent: :example() в следующем разделе.)

совет
       В данном случае существует экземпляр класса, а следовательно существуют и переменные, описанные в определении класса. Поэтому в данном случае вы можете использовать как $this, так и переменные — члены класса.

7.4.2. Доступ к методам предков с помощью parent

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

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

Вот как это выглядит на практике:
class А {
          function exampleO {<br>
               echo "Я главная функция А: : example().<br>\n";
                    }
               }
class В extends A {
          function exampleO {
               echo "А я В:: example и помогаю работать функции"
                    " A: :example.<br>\n" ;
               parent: :example() ;
                    }
               }
$b = new В;

// Вызов В: : exampleO , которая вызовет A: :example() .
$b->example() ;

7.5.Сериализация объектов

В седую старину, то есть в эпоху РНР 3, в процессе сериализации и последующего восстановления экземпляры класса теряли связь с определениями классов, поэтому само понятие сериализации в РНР 3 было весьма условным. Но в РНР 4 сериализация реализована уже по-другому, что превращает ее в эффективный механизм сохранения объектов на диске.

7.5.1 . Функции serialize() и unserialize()

Основной рабочей лошадкой механизма сериализации является функция serialize(), которая возвращает строку, содержащую побайтовое представление любого значения, которое может быть сохранено в РНР. Аналогичным образом функция unserialize() может использовать эту строку для восстановления первоначальнoй структуры данных. Использование serialize применительно к экземпляру класса приведет к сохранению всех переменных экземпляра. Конечно, функции сохранены не будут, но в отличие от РНР 3 в формируемую строку будет также помещено имя класса.

Чтобы иметь возможность восстановить экземпляр с помощью unserialize() необходимо, чтобы на момент вызова этой функции был определен класс, к которому принадлежит восстанавливаемый экземпляр. То есть если в программе stepl.php вы имели объект $а класса А и выполнили его сериализацию, вы получите строку, которая ссылается на класса А и содержит все значения переменных, которые содержались в экземпляре $а. Если затем в программе step2.php мы попытаемся восстановить значение $а, то эта программа также должна иметь описание класса А. Обычно в таких случаях определение классов помещается в отдельный включаемый файл, который затем используется в рабочих программах. Вот пример, демонстрирующий использование сериализации:

<?
// Описание класса А - classa.inc:
          class A {
               var $one » 1;

               function show_one() {
                    echo $this->one;
                         }
                    }
               ?>
               <?

// Сохранение объекта - stepl.php:
1nclude("classa.1nc");
a = new A;
s = serialize(a) ; // помещаем s там, где step2.php сможет его затем отыскать
// и куда может записать данные пользователь nobody (!)
fp » fopen("/tmp/store", "w");
fwrlte(fp.s);
echo s;
fclose(fp);
?>
<?
// Восстановление объекта - step2.php:
include("classa.inc");
s = implode("", @file("/tmp/store"));
a = unserialize(s);
// Применяем функцию-член show_one
a->show_one();
?>

Результаты выполнения программ stepl.php и step2.php приведены на рис. 7.3 и 7.4.

Рис. 7.3. Работа механизма сериализации: сохранение объекта во внешнем дисковом файле

Рис. 7.4. Работа механизма сериализации: восстановление объекта

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

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

примечание
       Итак, если в рассмотренном выше примере $а объявляется как часть сеанса с помощью вызова session_register("a"), вы должны включать файл classa.inc во все страницы, а не только в stepl .php и step2.php.

7.5.2. Специальные функции_sleep и_wakeup

Функция serialize () автоматически проверяет, не содержит ли определение сохраняемого класса функцию с зарезервированным именем_sleep. В случае обнаружения такой функции она автоматически запускается на выполнение перед началом сериализацш. К числу выполняемых ею задач обычно относится очистка отдельных полей объекта, и предполагается, что в результате ее работы будет возвращен массив, в котором содержатся имена всех переменных, подлежащих сериализации.

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

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

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

7.6. Ссылки в конструкторе

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

Перед нами пример определения класса:
class foo {
          function foo($name) {
               // ссылка внутри глобального массива $globalref
               global $globalref;
                    $globalref[] = &$this;
               // устанавливаем имя равным переданному значению
               $th1s->setName($name);
                    // и выводим его на печать
               $th1s->echoName();
          }
          function echoName() {
               echo "<br>",$th1s->Name;
          }
          function setName($name) {
               $th1s->Name = $name;
               }
}

Теперь давайте взглянем, нет ли разницы между экземпляром $ bаr1, который создается с помощью оператора копирования =, и $bаr2, созданным с помощью ссылочного оператора =&. Для этого мы воспользуемся следующим фрагментом:

$barl = new foo('Установлено в конструкторе');
$barl->echoName():
$globalref[0]->echoName();

/* Результат:
       Установлено в конструкторе
       Установлено в конструкторе
       Установлено в конструкторе */

$bаr2 =& new foo( 'Установлено в конструкторе');
$bar2->echoName();
$globalref[l]->echoName();

/* Результат:
       Установлено в конструкторе
       Установлено в конструкторе
       Установлено в конструкторе */

На первый взгляд никакой разницы не видно, но на самом деле различия есть, и весьма существенные! Дело в том, что $bаr 1 и $globalref [0] не являются ссылками на одну и ту же структуру данных — это разные переменные. Такое поведение обусловлено тем, что оператор new по умолчанию создает копию объекта, а не простую ссылку на него.

Никакой потери производительности из-за копирования не происходит, поскольку в РНР 4 используется механизм контроля количества ссылок, аналогичный используемому вPerl и Лиспе. Кроме того, работать с копиями зачастую оказывается предпочтительнее, поскольку как раз создание ссылок требует больше времени, чем простое копирование.

Взгляните на следующий пример как подтверждение вышеизложенного:
// Как вы думаете, что произойдет, если мы изменим имя?<br>
// Вы рассчитываете, что и $bar, и $globalref [0]
// изменят свои имена?

$barl->setName( 'Внешняя установка'};

// Как же! Это не тот случай...
$barl->echoName() ;
$globalref [0]->echoName():

/* Результат:
       Установлено в конструкторе
       Внешняя установка */

// А что произойдет в случае $Ьаr2 и $globalref [1]
$bar2->setName( 'Внешняя установка') ;

// поскольку $bar2->Name и $globalref [1] ->Name
// являются фактически одной и той же переменной
$bar2->echoName() ;
$globalref[l]->echoName();

/* Результат:
       Внешняя установка
       Внешняя установка */

И в заключение еще один пример, который оставляю читателю в качестве домашнего задания:

class a {
          function a($1) {
               $th1s->value * $i ;
               // подумайте, почему нам не надо использовать
               // здесь ссылку?
               $th1s->b = new b($th1s);
          }
          function createRef() {
               $th1s->c = new b($th1s);
                    }

          function echoValue() {
               echo " ", "class ".get class($th1s) , ": ",$th1s->value;
                    }
class b {
          function b(&$a) {
               $th1s->a - &$a;

          function echoValue() {
               echo "<br>","class ".get class($th1s) . ": ",$th1s->a->value;

// Подумайте, почему использование простой операции копирования в
// строке, отмеченной комментарием *, приводит к. неверному результату
$a =& new a(10) ;
$a->createRef () ;
$a->echoValue();
$a->b->echoValue(:)
$a->c->echoValue():

$a->value = 11;

$a->echoValue();,
$a->b->echoValue(); // *
$a->c->echoValue();

Результат :
       class a: 10
       class b: 10
       class b: 10
       class a: 11
       class b: 11
       class b: 11
*/
назад
далее

Сайт управляется системой uCoz