Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Очень просто о PHP, от элементарных понятий до ООП


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

Итак, продолжим:


Алфавитный суп

Теперь вы значете как создавать свои собственные функции в PHP и потратили несколько дней на перевод повторяющихся фрагментов своего кода в функции. Но функции - это только верхушка айсберга абстракции программирования. Сегодня мы заглянём под три буквы, внушающие ужас большинству новичков в программировании (не те, что вы подумали).

ООП.

Если вы уже немного программировали, вы возможно слышали это термин - ООП (объектно-ориентированное программирование), посредством которого создаются программные "объекты" и затем эти обьекты используются для построения нужной функциональности вашей программы. PHP 5 весьма силён в ООП - он имеет новую обьектную модель, которая приводит объекты в PHP в соответствие с принципами ООП и даёт ООП-программисту целый букет новый свойств, с которыми можно работать.

Надеетесь узнать побольше по этому вопросу? Больше не надейтесь. Ваши молитвы были услышаны.

В этом уроке я собираюсь предпринять краткий обзор возможностей ООП в PHP (как PHP 4, так и PHP 5) с примерами и пояснениями, чтобы показать насколько оно мощно. Мы охватим большую часть основ - классы, обьекты, атрибуты и методы - и несколько более сложные концепции - конструкторы, деструкторы, частные методи с свойства, а также наследование. Если вы новичок в объектно-ориентированном программировании, или просто опасаетесь того, что васждет впереди, не волнуйтесь - я обещаю, это будет гораздо менее болезненно чем вы думаете. И в отличие от стоматолога, я не вру.

Назад в класс

Перед началом давайте убедимся в том, что у вас есть чёткое представление об изучаемых концепциях

В PHP класс - это просто набор программных выражений, которые выполняют определённую задачу. Типичное определение класса содержит и переменные, и функции и служит в качестве шаблона, из которого порождаются экземпляры класса.

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

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

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

В мире животных

Чтобы лучше это понять, давайте возьмём животное, любое. Я выберу медведя так как я люблю медведей. Спросите себя, можете-ли вы описать медведя в рамках ООП как "обьект"?

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

Давайте пойдём чуть дальше. Так как у всех медведей одинаковые характеристики, то становится возможным создать шаблон Bear(), который определяет простые свойства и возможности каждого медведя на планете. После того, как Bear() (класс) использован для создания $bear (обьект), индивидуальные характеристики новосозданного медведя могут быть изменены независимо от других медведей, которых можно насоздавать из шаблона огромное количество.

Теперь, если перевести всё это в PHP-код, то оно будет выглядет так:

<?php

// PHP 5 

// определение 
class Bear { 
    // определим свойства
    public $name; 
    public $weight; 
    public $age; 
    public $sex; 
    public $colour; 

    // определим методы 
    public function eat() { 
        echo $this->name." спит... "; 
    } 

    public function run() { 
        echo $this->name." бежит... "; 
    } 

    public function kill() { 
        echo $this->name." ловит добычу... "; 
    } 

    public function sleep() { 
        echo $this->name." спит... "; 
    } 
}

?>

Дан класс, теперь просто создадим столько медведей сколько нам надо, и указажем разные свойства каждому. Посмотрите:

<?php

// мой первый медведь, папа
$daddy = new Bear; 
// дадим ему имя
$daddy->name = "Михаил Потапыч"; 
// сколько ему лет
$daddy->age = 8; 
// укажем пол
$daddy->sex = "М"; 
// цвет шкуры
$daddy->colour = "чёрная"; 
// сколько он весит
$daddy->weight = 300; 

//жена папочки 
$mommy = new Bear; 
$mommy->name = "Мамаша Маша"; 
$mommy->age = 7; 
$mommy->sex = "Ж"; 
$mommy->colour = "чёрная"; 
$mommy->weight = 310; 

// и малыш 
$baby = new Bear; 
$baby->name = "Мишутка"; 
$baby->age = 1; 
$baby->sex = "М"; 
$baby->colour = "коричневая"; 
$baby->weight = 180; 

// однажды прекрасным летним вечерком
// папа поймал рыбку и принёс её домой 
$daddy->kill(); 

// мама её ест 
$mommy->eat(); 
// и малыш тоже
$baby->eat(); 

// мама спит 
$mommy->sleep(); 
// и папа спит
$daddy->sleep(); 

// малыш втихаря есть ещё немножко рыбки
$baby->eat();

?>

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

Всё выше, и выше, и выше

Теперь, раз вы твёрдо условили концепцию, давайте рассмотрим мелкие детали в определениях класса.

<?php

// PHP 5 

// определение класса 
class Bear { 

    // определим публичные свойства
    public $name; 
    public $age; 

    // ещё свойства...

    // определим публичные методы
    public function eat() { 
        echo $this->name." есть... "; 
        // ешё код...
    } 

    // ещё методы...
}

?>

Каждое определение класса начинается с ключевого слова class, за которым следует имя класса. Вы можете давать своему классу любое остроумное имя, лишь-бы оно не было зарезервированным словом в PHP. Пара фигурных скобок содержит все переменные и функции класса, которые описываются обычным кодом PHP.

PHP 5 водит понятие видимости обьектной модели. Видимость управляет тем, в какой степени свойства и методы обьекта могут быть изменены вызвавшим их кодом и играют важную роль в определении того, насколько открыт или закрыт ваш класс. Существует три уровня видимости, от более до менее видимого: публичный, приватный и защищённый. В определении класса вы можете указывать видимость свойства или метода одним из ключевых слов: public, private, или protected.

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

Так как обьектная модель PHP 4 не поддерживает видимость, определение класса в нём не будет работать. Вместо этого вам надо использовать такой код:

<?php

// PHP 4 

// определение класса
class Bear { 
    // определяем свойства
    var $name; 
    var $weight; 
    var $age; 
    var $sex; 
    var $colour; 

    // определяем методы 
    function eat() { 
        echo $this->name." естg... "; 
    } 

    function run() { 
        echo $this->name." бежит... "; 
    } 

    function kill() { 
        echo $this->name." добывает пропитание... "; 
    } 

    function sleep() { 
        echo $this->name." дрыхнет... "; 
    } 
}
?>

Из примера должно быть понятно, что свойства и методы в PHP 4 всегда публичны... и с этим вы ничего не можете поделать!

Чтобы создать новый экмепляр класса, вы используете ключевое слово new для привязки создаваемого обьекта к переменной PHP.

<?php

$daddy = new Bear;

?>

На человеческом языке это означает "создать новый обьект класса Bear() и назначить его переменной $daddy".

Теперь вы имеете доступ ко всем методам и свойствам класса в этой переменной. Например, код

<?php

$daddy->name = "Михайло Потапыч";

?>

означает "назначить значение "Михайло Потапыч" переменной $name указанного обьекта класса Bear(), а код

<?php

$daddy->sleep();

?>

означает "выполнить функцию sleep() для этого экземпляра класса Bear()".

Обратите внимание, символ ->используется для связки обектов с их свойствами и методами, а символ $ при их вызове пропускается.

Это и то

В случае, если вам надо получить доступ к функциям и переменным изнутри самого класса, и PHP 4, и PHP 5 предлагают ключевое слово $this, которое используется для ссылки на "этот" класс. Чтобы понять как это работает, давайте изменим метод eat() так, чтобы он брал количество сьеденного и добавлял к весу медведя.

<?php

// PHP 5 

// определение класса 
class Bear { 
    // определим свойства
    public $name; 
    public $weight; 

    // определим методы
    public function eat($units) { 
        echo $this->name." ест ".$units." кг мёду... "; 
        $this->weight += $units; 
    } 
}

?>

Здесь префикс $this показывает что переменная, которую надо изменить, находится внутри класса, или по-русски: "добавить аргумент, переданный функции eat() к переменной $weight данного обьекта".

Пример как это работает:

<?php

// создадим обьект 
$baby = new Bear; 
$baby->name = "Мишутка"; 
$baby->weight = 1000; 

// создадим ещё один обьект
// у него другие значения каждого свойства
$brother = new Bear; 
$brother->name = "Топтыжка"; 
$brother->weight = 1000; 

// получим свойства 
echo $baby->name." весит ".$baby->weight." кг "; 
echo $brother->name." весит ".$brother->weight." кг "; 

// вызываем метод eat() 
$baby->eat(100); 
$baby->eat(50); 
$brother->eat(11); 

// получем новые значения
echo $baby->name." теперь весит ".$baby->weight." кг "; 
echo $brother->name." теперь весит ".$brother->weight." кг ";

?>

Результат будет такой:

Мишутка весит 1000 кг

Топтыжка весит 1000 кг

Мишутка ест 100 кг мёду

Мишутка ест 50 кг мёду

Топтыжка ест 11 кг мёду

Мишутка теперь весит 1150 кг

Топтыжка теперь весит 1011 кг

Андер констракшен

Также есть возможность автоматически выполнять функцию при создании нового обьекта класса. На жаргоне компьютерщиков она называется конструктором, и чтобы её использовать, ваш класс в PHP 5 должен иметь функцию __construct().

Например, если мы хотим, чтобы новорождённый мишка сразу становился коричневым и имел вес 100 кило, мы добавим в определение класса такой код:

<?php

// PHP 5 

// определение класса
class Bear { 
    // определим свойства
    public $name; 
    public $weight; 
    public $age; 
    public $colour; 
     
    // конструктор 
    public function __construct() { 
        $this->age = 0; 
        $this->weight = 100; 
        $this->colour = "коричневый"; 
    } 
     
    // определим методы...
}

?>

В PHP 4 ваш конструктор должен иметь такое-же имя как и класс. Аналогичный код для PHP 4:

<?php

// PHP 4 

// определение класса
class Bear { 
    // определение свойств
    var $name; 
    var $weight; 
    var $age; 
    var $colour; 

    // конструктор
    function Bear() { 
        $this->age = 0; 
        $this->weight = 100; 
        $this->colour = "коричневый"; 
    } 
     
    // определим методы...
}

?>

Попробуем создать и использовать экземпляр класса:

<?php

// создаём экземпляр 
$baby = new Bear; 
$baby->name = "Мишутка"; 
echo $baby->name." - ".$baby->colour." и весит ".$baby->weight." кг при рождении"; 

?>

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

Мишутка - коричневый и весит 100 кг при рождении

Руки прочь!

Как было замечено раньше, PHP 5 позволяет обозначать свойства и методы класса как приватные. Это означает что они не могут быть доступны снаружи определения класса. Это полезно для защиты внутренностей класса от изменения обьектами класса. В соответствии со следующим примером, добавляющим новую приватную переменную $_lastUnitsConsumed, класс Bear() будет выглядеть так:

<?php

// PHP 5 

// определение класса
class Bear { 
    // определим свойства
    public $name; 
    public $age; 
    public $weight; 
    private $_lastUnitsConsumed; 

    // конструктор 
    public function __construct() { 
        $this->age = 0; 
        $this->weight = 100; 
        $this->_lastUnitsConsumed = 0; 
    } 
     
    // определим методі 
    public function eat($units) { 
        echo $this->name." ест ".$units." кг меда... "; 
        $this->weight += $units; 
        $this->_lastUnitsConsumed = $units; 
    } 

    public function getLastMeal() { 
        echo "Сьедено меда, килограммов: ".$this->_lastUnitsConsumed." "; 
    } 
}

?>

Теперь, так как переменная $_lastUnitsConsumed была обьявлена как приватная, любая попытка изменить её из экзмепляра обьекта будет неудачной. Вот пример:

<?php

$bob = new Bear; 
$bob->name = "Мишутка"; 
$bob->eat(100); 
$bob->eat(200); 
echo $bob->getLastMeal(); 
// следующая строка вызовет фатальную ошибку
$bob->_lastUnitsConsumed = 1000;

?>

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

Расширяемся

Две из лучших вещей в ООП, неважно в PHP 4 или в PHP 5, это расширяемость и наследование. По-простому, это означает что вы можете создать новый класс на основе сущесвтующего, добавилять новые возможности (читайте: свойства и методы) к нему, а затем создавать обьекты на основе нового класса. Эти обьекты будут иметь все возможности оригинального родительского класса вместе с новыми возможностями дочернего класса.

<?php

// PHP 5 

// определение класса
class Bear { 
    // define properties 
    public $name; 
    public $weight; 
    public $age; 
    public $sex; 
    public $colour; 

    // конструктор 
    public function __construct() { 
        $this->age = 0; 
        $this->weight = 100; 
    } 

    // define methods 
    public function eat($units) { 
        echo $this->name." ест ".$units." кг мёда... "; 
        $this->weight += $units; 
    } 

    public function run() { 
        echo $this->name." бежит... "; 
    } 

    public function kill() { 
        echo $this->name." зацапывает добычу... "; 
    } 

    public function sleep() { 
        echo $this->name." спит... "; 
    } 
} 

// определение расширенного класса (полярный медведь)
class PolarBear extends Bear { 

    // конструктор 
    public function __construct() { 
        parent::__construct(); 
        $this->colour = "белый"; 
        $this->weight = 600; 
    } 

    // define methods 
    public function swim() { 
        echo $this->name." плывёт... "; 
    } 
}

?>

Ключевое слово extends используется для расширения родительского класса в дочернем. Все функции и переменные родительского класса немедленно становятся доступными в дочернем. Это прекрасно видно в следующем фрагменте кода:

<?php

// экземпляр класса Bear() 
$tom = new Bear; 
$tom->name = "Мишутка"; 

// экземпляр класса PolarBear() 
$bob = new PolarBear; 
$bob->name = "Бориска"; 

// $bob может использовать все методы классов Bear() и PolarBear() 
$bob->run(); 
$bob->kill(); 
$bob->swim(); 

// $tom может использовать методы класса Bear(), но не PolarBear() 
$tom->run(); 
$tom->kill(); 
$tom->swim();

?>

Здесь последний вызов $tom->swim() вызовет ошибку, так как класс Bear() не содержит метода swim(). А вот $bob->run() или $bob->kill() не вызовут ошибки, так как поскольку $bob является экземпляром класса PolarBear(), дочернего по отношению к Bear(), который наследует все методы и свойства своего родителя.

Заметьте, что в конструкторе дочернего класса PolarBear() был вызван конструктор родительского класса parent::__construct(). Это хорошая идея делать так, чтобы вся необходимая начальная инициализация родительского класса происходила при создании дочернего класса. Специальная инициализация дочернего класса может быть сделана в конструкторе дочернего класса. Конструктор родительского класса будет вызываться автоматически только в том случае, если у дочернего класса нет собственного конструктора.

Вы можете делать то-же самое в PHP 4. Вот определение класса PolarBear для версии PHP 4:

<?php

// PHP 4 

// определение расширенного класса
class PolarBear extends Bear { 

    // конструктор 
    function PolarBear() { 
        parent::Bear(); 
        $this->colour = "белый"; 
        $this->weight = 600; 
    } 

    // определим методы
    function swim() { 
        echo $this->name." плывёт... "; 
    } 
}

?>

Для защиты класса или его методов от наследования, используйте ключевое слово final в имена класса или метода (работает в PHP версии не менее 5). Вот пример, который определяет класс Bear() как ненаследуемый (если есть такое слово):

<?php

// PHP 5 

// определение класса 
final class Bear { 
    // определим свойства

    // определим методы
} 

// определение расширенного класса
// вызовет ошибку, так как Bear() не может быть расширен
class PolarBear extends Bear { 
        // определим методы
} 

// создадим обьект класса PolarBear() 
// вызовет ошику, так как Bear() не может быть расширен
$bob = new PolarBear; 
$bob->name = "Бориска"; 
echo $bob->weight;

?>

Оригинал перевода статьи: http://www.figli-migli.org.ua/books/942-php101-7


В избранное