ООП (Статей: 27)

Статья ориентированна на опытных программистов потому содержит только теоретические сведения, реализацию написанного здесь могу предоставить совершенно бесплатно администратору сайта либо известным мне пользователям по просьбе.
Идея сохранять свойства экземпляров класса во внешней памяти появилась довольно давно. Совсем недавно полностью ее реализовал на PHP MySQL. Что из себя представляет система: реализована возможность сохранить экземпляр класса во внешней памяти компьютера, а затем "идентифицировать" экземпляр того же класса по известным свойствам.
Перейду к терминологии: Постоянным классом называется абстрактный класс в котором определены методы идентификации, добавления, удаления и изменения данных.
Идентификатор - независимый от остальных свойств атрибут однозначно идентифицирующий данный экземпляр класса в базе данных.
Идентификацию - метод позволяющий заполнить свойства экземпляра класса их базы данных по известным свойствам (на пример определить рейтинг пользователя по логину).
Удаление - метод позволяющий стереть данные экземпляра класса из хранилища объектов (базы данных).
Добавление - метод позволяющий добавить данные экземпляра класса в хранилище объектов.
Изменение - метод позволяет изменять любое свойство идентифицированного объекта кроме идентификатора.
Теперь немного о плюсах парадигмы: подобный подход позволяет хранить объекты в реляционной базе данных, востанавливать их свойства в любой момент времени, создавать связи любой сложности и вложенности, устраняет необходимость составления запросов для идентификации объектов.
На последок приведу реальный пример использования парадигмы для авторизации пользователя:
$клиент = new Клиент();
$клиент->логин = 'Башка';
$клиент->пароль = 'абв';
$клиент->авторизировать();
/*
этого кода вполне достаточно чтобы идентифицировать клиента по заданному логину и паролю. Заранее был определен дочерний класс Клиент с перегрузкой методов идентификации и добавления
*/
Новая объектно-ориентированная модель в PHP5
Когда Зив Сераски (Zeev Suraski) добавил объектно-ориентированный (ОО) синтаксис в PHP 3, это можно было рассматривать как "синтаксический подсластитель для поддержки классов" ("syntactic sugar for accessing collections"). Объектно-ориентированная модель получила поддержку наследования и позволяла классу (и объекту) объединять методы и свойства, но не более того. Когда Зив и Анди переписали движок для PHP 4, это был полностью новый движок, работающий намного быстрее, намного стабильнее и с еще многими другими возможностями. Однако, изменения практически не затронули ОО модель, первоначально введенную еще в РНР 3.
Хотя объектная модель имела серьезные ограничения, она широко использовалась, часто в очень больших приложениях, написанных на PHP. Это победное шествование парадигмы ООП, даже такой ограниченной в РНР 4, привело к тому, что изменения объектной модели стали центральными в новом релизе РНР - PHP5.
Какие были ограничения в PHP 3 и 4? Самым большим ограничением (которое и приводило ко всем остальным ограничениям) был тот факт, что семантика экземпляра объекта была такой же, что и для родных типов. Как это фактически отражалось на разработчиках? Когда вы присваивали переменную (которая указывает на объект) другой переменной, то создавалась копия объекта. Мало того, что это влияло на производительность, но и это обычно приводило к ошибкам в приложении, потому что многие разработчики думали, что обе переменные будут указывать на тот же самый объект. А они указывали на разные копии того же самого объекта, поэтому, изменяя один объект, мы не меняли другой. Вот пример:
<?php 
class Person { 
     var $name; 
     function getName() { 
         return $this->name; 
     } 
     function setName($name) { 
         $this->name = $name; 
     } 
     function Person($name) { 
         $this->setName($name); 
     } 
} 

function changeName($person, $name) { 
     $person->setName($name); 
} 

$person = new Person("Andi"); 
changeName($person, "Stig"); 
print $person->getName(); 
?>
В РНР 4 этот код выведет "Andi". Причина кроется в том, что мы передаем объект $person в функцию changeName() по значению, а не по ссылке, таким образом, объект $person будет скопирован, и changeName() будет работать уже с копией объекта $person.
Такое поведение не является интуитивно понятным. Действительно, многие разработчики ожидали Java-подобного поведения. В Java, переменные фактически являются указателями на объект, и поэтому при дублировании будет скопирован указатель, а не сам объект.
Было два вида разработчиков: те, кто знал об этой проблеме, и те, кто не знал. Последние, обычно, не сталкивались с этой проблемой, потому что их код был написан так, что было безразлично, существует ли такая проблема или нет. Конечно, некоторые из этих разработчиков проводили бессонные ночи в "увлекательных" поисках "сверхъестественных" ошибок. Первая группа также имела проблему, поскольку приходилось вручную определять передачу объекта по ссылке, запрещая движку копировать объекты, и код был испещрен многочисленными знаками '&'.
Старая объектная модель приводит не только к вышеупомянутым проблемам, но также вскрывает более фундаментальные проблемы, которые на существующей объектной модели не позволяли осуществлять другие возможности.
В PHP 5 объектная модель была полностью переписана для того, чтобы сразу работать с указателями на объект. Если вы явно не клонируете объект, используя ключевое слово clone, вы никогда не будете работать с копией объекта, думая, что работаете с самим объектом. В PHP 5 уже не нужно явно передавать объекты или присваивать их по ссылке, это делается автоматически.
Обратите внимание: явная передача и присваивание по ссылке также поддерживается, на тот случай, если вы хотите изменить содержимое переменной или объекта.
Новый объектно-ориентированный подход в PHP5
Новые возможности объектной модели являются слишком многочисленными. Приведем обзор главных изменений:
public/private/protected - модификаторы доступа для методов и свойств
Позволяют управлять доступом к методам и свойствам. Теперь видимость свойств и методов может быть определена ключевыми словами: public, private, protected. Модификатор 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; // Works
echo $obj->protected; // Fatal Error
echo $obj->private; // Fatal Error
$obj->printHello(); // Shows Public, Protected and Private


/**
  * Define MyClass2
  */
class MyClass2 extends MyClass
{
     // We can redeclare the public and protected method, but not private
     protected $protected = 'Protected2';

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

$obj2 = new MyClass2();
echo $obj->public; // Works
echo $obj2->private; // Undefined
echo $obj2->protected; // Fatal Error
$obj2->printHello(); // Shows Public, Protected2, not Private

?>
Унифицированный конструктор __construct()
PHP 5 позволяет объявлять методы-конструкторы. Классы, в которых объявлен метод-констуктор, будут вызывать этот метод при каждом создании нового объекта, так что это может оказаться полезным, чтобы, например, инициализировать какое-либо состояние объекта перед его использованием. Конструктор, ранее совпадавший с названием класса, теперь необходимо объявлять как __construct(), что позволит легче перемещать классы в иерархиях. Конструкторы в классах-родителях не вызываются автоматически. Чтобы вызвать конструктор, объявленный в родительском классе, следует обратиться к методу 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().
Поддержка деструктора для класса, определяемого как метод __destructor()
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().
Создаем класс который мы назовем Template
<?php
    class Template {
}
?>
Теперь мы укажем свойства которые нам пригодятся!
<?php
class Template {
    private $_arr = array();
    private $_content;
    public  $template_dir = 'templates/';
}
?>
Теперь мы объявляем метод, который нам позволяет присваивать значения переменным, доступным в шаблоне.
Т.е мы в массив пишем все указанные переменные.
Для чего это нам необходимо увидите ниже =)
<?php
public function __set($var, $name) {
    $this -> _arr[$var] = $name;
}
?>

Следующим шагом у нас будет парсинг.
<?php
private function parsing($tpl) {
    /*
    * Открываем шаблон.
    */
    $this -> _content = file_get_contents($this -> template_dir.$tpl);
    /*
    * И вот теперь можно увидеть для чего нам нужен был метод __set()
    * как видите в свойстве _arr у нас хранится то что нам надо заменить.
    * Теперь мы обрабатываем переменные в шаблоне, такого вида {$name}
    */
    foreach ($this -> _arr as $key => $name) {
        $tpl = str_replace('{$'.$key.'}', $name, $this -> _content);
    }
    return $tpl;
}
?>

Когда у нас все готово, мы пишем метод для вывода шаблона, и сразу обрабатываем его.
<?php
public function display($tpl) {

    /*
    * Проверяем существует ли заданный шаблон или нет.
    */
    if (is_file($this -> template_dir.$tpl)) {

      $this -> _content = parsing($this -> template_dir.$tpl);
       /* 
       * И теперь выводим шаблон
       */
       echo $this -> _content;
       /* 
       * В случае если файла с шаблоном не найдено, выводим ошибку.
       */
    } else {
        echo '<p> Неверный путь к шаблону </p>';
    }
}
?>

Используем шаблон так:
<?php
/* 
* Создаем объект 
*/
$template = new Template;
/* 
* При необходимости указываем категорию с шаблонами.
* По умолчанию стоит категория templates/
*/
$template -> template_dir = 'my_dir/templates/';
/* 
* Указываем какую переменную на что заменить.
*/
$template -> name = 'Hello world =)';
/* 
* Выводим шаблон.
*/
$template -> display('template.tpl');

?>

# Nu3oN
Готовый пример можно скачать тут: Example
Когда функций очень много их неудобно хранить все в одном файле, да и зачем подключать километр кода когда нужно воспользоваться всего лишь парой функций, собственно решение:

Cоздаем в корне папку fun в которой будут хранится наши функции, по одному файлу на каждую, имя файла должно соответствовать имени функции, для примера создадим файл test.php с функцией test

<?php
function test($val1, $val2, $val3){
	return $val1.$val2.$val3;
}
?>

Теперь класс который будет подключать наши функции
<?php
class fun{
	function __call($function, $value) {
		include_once $_SERVER['DOCUMENT_ROOT'].'/fun/'.$function.'.php';
		return call_user_func_array($function, $value);
	}
}
?>

Теперь воспользуемся нашей функцией "test"
<?php
$fun = new fun;
echo $fun->test('тра ','та ', 'та ');
//Выведет тра та та
?>

Вот и всё, проще же подключить всего один маленький класс вместо километра кода и вызывать функции $fun->ИмяФункцииКоторуюНужноВызвать(параметры);
И ещё, не мешало бы создать файл в той же папке fun с описанием каждой функции это поможет и вам и тем кто будет потом работать с вашим скриптом

Тест производительности

Провел тест, создал 1к функций
<?php
function function0($msg)
{
    $eng = array("_", "YA", "Ya", "ya", "yee",
        "YEE", "YO", "yo", "Yo", "ZH", "zh", "Zh",
        "Z", "z", "CH", "ch", "Ch", "SH", "sh",
        "Sh", "YE", "ye", "Ye", "YU", "yu", "Yu",
        "JA", "ja", "Ja", "A", "a", "B", "b", "V",
        "v", "G", "g", "D", "d", "E", "e", "I", "i",
        "J", "j", "K", "k", "L", "l", "M", "m", "N",
        "n", "O", "o", "P", "p", "R", "r", "S", "s",
        "T", "t", "U", "u", "F", "f", "H", "h", "W",
        "w", "x", "q", "Y", "y", "C", "c");
    $rus = array(" ", "Я", "Я", "я", "ые", "ЫЕ",
        "Ё", "ё", "Ё", "Ж", "ж", "Ж", "З", "з", "Ч",
        "ч", "Ch", "Ш", "ш", "Ш", "Э", "э", "Э", "Ю",
        "ю", "Ю", "Я", "я", "Я", "А", "а", "Б", "б",
        "В", "в", "Г", "г", "Д", "д", "Е", "е", "И",
        "и", "Й", "й", "К", "к", "Л", "л", "М", "м",
        "Н", "н", "О", "о", "П", "п", "Р", "р", "С",
        "с", "Т", "т", "У", "у", "Ф", "Ф", "Х", "х",
        "Щ", "щ", "ъ", "ь", "Ы", "ы", "Ц", "ц");
    return str_replace($eng, $rus, $msg);
}
//function1
//function2
//function3 итд
В каждом файле запустил 100 применений функций

test1.php - Тут все функции подключаются по старинке одним инклюдом
function0('abc');
function1('abc');
function2('abc'); итд

test2.php - Мой метод в котором происходит 100 инклюдов
$fun->function0('abc');
$fun->function1('abc');
$fun->function2('abc');
$fun->function3('abc'); итд



Первый запуск
/test1.php 0.230176925659/18261 кб
Последующие
/test1.php
0.206670999527/18262 кб //И стабильно Держится не падает ниже 2

Первый запуск
/test2.php 0.506945848465/1965 кб
Последующие
/test2.php 0.0756809711456/1965 кб //Держится стабильно

Хотя и первый запуск немного проигрывает в скорости, но вот в количестве занятой оперативки выигрывает на 90%
А в последующих запусках он вообще лидирует во всем
Что лучше, по моему очевидно smile
Основные понятия
В объектно-ориентированном программировании выделяют три основных элемента: инкапсуляция, наследование, полиморфизм. Статья не ставит своей целью всестороннее рассмотрение всех аспектов ООП. Здесь лишь кратко будет рассмотрена их суть.
Инкапсуляция. Инкапсуляция – это скрытие реализации. Для пользователей класса неважно как реализован класс, важено лишь какие методы доступны, т.е. какой интерфейс представляет класс. Мы уже дважды встречали инкапсуляцию. В первом случае мы объявили поля класса как закрытые (private), т.е. скрыли их от посторонних глаз. Методы также можно делать закрытыми (private), они не будут доступны для внешнего пользователя, однако их можно будет вызывать внути открытых (public) методов этого же класса. Закрытые методы необходимы для того, чтобы упростить открытый методы метод, разбив сложную задачу на несколько более простых. В первую очередь это необходимо для реорганизации кода, для более удобного его чтения и понимания. Во втором случае это также закрытое поле $db. Мы присвоили внутренней переменной соответствующий тип данных, скрыв не только реализацию, но и процесс создание объекта.
Замечание
Объект можно создать и внутри класса, например, в конструкторе.
Управление инкапсуляцие осуществляется при помощи модификаторов доступа – их три:
Public – поля и методы видны везде – в самом классе, в классе потомке, просто во внешнем участке кода, использующем класс.
Private – данные видны только в том классе, в котором они определены.
Protected – данные видны как в том классе, в котором они определены, так и в классе, который наследует первоначальный класс, однако для внешнего кода данные не доступны.
Наследование. В косвенной форме мы уже сталкивались со связью классов друг с другом, когда объявляли переменную типа DataBase. Мы неявно произвели связывание с помощью включения. Но что же такое наследование в более обычном его понимании? Под наследованием понимается расширение старого класса до нового путём расширения функциональности, т.е. новый класс будет содержать те же методы (которые, кстати, могут и изменить свою реализацию) и свойства прежнего класса.
Новый класс мы называем классом-потомком, а прежний, от которого происходит наследование, классом-предком или базовым классом. Наследование используется, в первую очередь, для построения иерархических систем, в котором классы-потомки развивают функциональность базовых классов. Наследование также может использоваться для изменения логики первоначальной реализации - т.е. для модификации уже существующего приложения.
Рассмотрим пример. Пусть необходимо расширить перечень параметров, которые может ввести пользователь в гостевой книге. Пусть помимо имени, email и сообщения пользователи получают возможность вводить адрес Web-сайта и номер ICQ. Для этого не требуется создавать новый класс, мы просто наследуем уже готовую реализацию от класса GuestBook.
<? 
class SharedGuestBook extends GuestBook 
{ 
    private $url; 
    private $icq; 
     
    public function __construct($name, $email, $msg, $url, $icq) 
    { 
        parent :: __construct($name, $email, $msg); 
        $this->url = $url; 
        $this->icq = $icq; 
    } 
    public function getUrl() 
    { 
        return $this->url; 
    } 
    public function getIcq() 
    { 
        return $this->icq; 
    } 
} 
?>
Как видно из листинга, старые методы не нужно реализовывать - именно так достигается одна из важнейших задач объектно-ориентированного подхода - повторное использование кода.
Обратите внимание на реализацию конструктора - parent :: __construct($name, $email, $msg); - обратимся к официальной документации (http://www.php.net/manual/ru/language.oop5.paamayim-nekudotayim.php): Когда дочерний класс перегружает методы, объявленные в классе-родителе, PHP не будет осуществлять автоматический вызов методов, принадлежащих классу-родителю. Этот функционал возлагается на метод, перегружаемый в дочернем классе. Данное правило распространяется на [url = http://www.php.net/manual/ru/language.oop5.decon.php]конструкторы и деструкторы[/url], перегруженные и "магические" методы.
С помощью ключевого слова extends как раз и осуществляется наследование. Теперь настало время изменить класс GuestBookDb для работы с новым типом данных.
<? 
class SharedGuestBookDb extends GuestBookDb 
{ 
    public function Select() 
    { 
        $sql = "SELECT name, email, msg, url, icq FROM new_guestbook"; 
        $dbArray = $this->db->Db2Array(); 
        foreach($dbArray as $rows) 
        { 
            $outPut[] = new SharedGuestBook($rows['name'], $rows['email'], $rows['msg'], $rows['url'], $rows['icq']); 
        } 
        return $outPut; 
    } 
    public function Insert($obj) 
    { 
        $name = $obj->getName(); 
        $email = $obj->getEmail(); 
        $msg = $obj->getMsg(); 
        $url = $obj->getUrl(); 
        $icq = $obj->getIcq(); 
         
        $sql = "INSERT INTO new_guestbook (name, email, msg, url, icq) VALUES('$name', '$email', '$msg', '$url', '$icq')"; 
        if($this->db->Insert($sql) === TRUE) 
            return TRUE; 
        return FALSE; 
    } 
} 
?>
В данном случае нет необходимости перегружать конструктор, т.к. он остался неизменным. Мы просто переопределили необходимые методы – изменили их функционал, оставив их предыдущие названия, а это уже полиморфизмом пахнет smile
При наследовании мы можем переопределить любой метод, но иногда нам надо запретить переопределение методов, оставив первоначальную реализацию. Для подобного случая необходимо использовать ключевое слово final. Рассмотрим пример.
<? 
class SomeClass 
{ 
    private $var; 
     
    final public function PrintVar() 
    { 
        echo $var; 
    } 
    public function SetVar() 
    { 
        $this->var = "From SomeClass"; 
    } 
} 
class SomeClassNew extends SomeClass 
{ 
    public function SetVar() 
    { 
        $this->var = "From SomeClassNew"; 
    } 
} 
?>
Если мы бы начали переопределять метод PrintVar(), то возникла бы ошибка.
Также мы можем объявлять и классы с ключевым словом final, если будет необходимость запретить наследование от данного класса.
Полиморфизм. Полиморфизмом называется возможность применения методов с одинаковыми именами в группе классов, связанных наследованием.
Конструкторы и деструкторы
Конструктор. Мы уже немного знакомы с конструкторами. Давайте познакомимся с ними поближе. И так, конструкторы, в сущности являющиеся специальными методами, вызываются каждый раз при создании объекта класса, в котором определен конструктор. В классе может и не быть конструктора, а также может быть только один конструктор. Обычно конструктор используют для инициализации какого-либо поля класса, т.е. для задания первоначального состояния объекта, например, с помощью вызова некоторых методов, как правило, приватных. Если конструктор содержит параметры, то и объект должен быть вызван с соответствующими параметрами. Естественно, если мы объявим метод с параметрами, то и вызвать его должны с тем же количеством параметров, а, как уже говорилось выше, конструктор – специальный метод, который вызывается при создании объекта. Поэтому и объект мы должны создавать с параметрами. Давайте создадим объект для нашего класса GuestBook
<? 
$guest_book = new GuestBook($name, $email, $msg); 
?>
Если бы не было конструктора или конструктор не содержал бы параметров, то мы создавали бы объект так:
<? 
$var = new SomeClass(); 
?>
Нам также известно из предыдущих примеров как вызывать конструкторы, объявленные в родительских классах – parent :: __construct();
Деструктор. Деструктор вызывается при уничтожении объекта. Деструктор не может содержать параметры. Как и конструктор, деструктор тоже специальный метод со специальным именем __destruct(). Обычно деструктор используется для очищения памяти от различного мусора: закрытие соединения с базой данных, закрытие файла, удаление ненужных переменных и т.д. Для вызова деструктора класса-предка, по аналогии с конструктором, - parent :: __destruct();
Моделирование на более высоком уровне абстракции. Абстрактные классы и интерфейсы.
Архитектор начинает свой проект с макета здания, с чертежа. Программист же начинает проект с абстрактной модели. Мы уже моделировали гостевую книгу, но на наименьшем уровне абстракции. Вспомним, что класс является абстрактным типом данных, поэтому абстракция имеет достаточно важное значение. Мы проектируем сущность, но мы еще не знаем или пока не хотим затрагивать реализацию, поэтому мы только делаем макеты методов, которые будут работать с данными. На первоначальном этапе нам надо только описать функциональность класса, оставляя детали реализации. Нам просто надо понять то, что мы хотим сделать над теми данными, которые мы включили в наш тип. Также мы заботимся о тех программистах, которые будут или разрабатывать логику методов, или использовать наши будущие методы, или как-то иначе использовать наш тип данных. Для организации вышесказанного на арену выходят абстрактные классы, их абстрактные методы, а также интерфейсы.
Если в классе объявлены абстрактные методы, то и класс тоже должен быть объявлен как абстрактный. Абстрактный класс может содержать и обычные методы и поля. Нельзя создавать объект абстрактного класса, но его нужно переопределять классами потомками. Т.е. абстрактные классы служат прототипами классов, которые наследуют их основу. Мы безоговорочно должны принять правила, диктуемые нам абстрактными классами. Если мы объявили абстрактный метод с двумя параметрами, то в последующих классах мы должны реализовать одноименный метод именно с тем же числом параметров.
Создадим абстрактный класс для класса GuestBookDb, а класс SharedGuestBookDb мы можем унаследовать как от абстрактного класса, так и от класса GuestBookDb.
<? 
abstract class GbDb 
{ 
    private $db; 
     
    public function __construct($db) 
    { 
        $this->db = $db; 
    } 
     
    abstract public function Select(); 
    abstract public function Insert($obj); 
} 
class GuestBookDb extends GbDb 
{ 
    public function Select() 
    { 
        // реализация 
    } 
    public function Insert($obj) 
    { 
        // реализация 
    } 
} 
class SgbDb extends GuestBookDb 
{ 
    public function Select() 
    { 
        // реализация 
    } 
    public function Insert($obj) 
    { 
        // реализация 
    } 
} 
?>
Абстрактные классы выгодны тогда, когда в них содержится менее абстрактная реализация, например, как в нашем примере. А если нам необходима только абстракция? И нужно унаследовать класс от нескольких абстрактных сущностей, мы же помним, что наследовать мы можем только от одного класса? И на сцену выходят интерфейсы. В интерфейсе можно объявлять без спецификаторов, в т.ч. и abstract. В интерфейсе можно определять только абстракцию действий, т.е. методы, которые могут быть только публичными. Действительно, мы же абстрагируем видимую реализацию. Нам надо показать не только себе, но и другим разработчикам работу нашего класса. Давайте рассмотрим интерфейс на практике. Немного изменим наш предыдущий код.
<? 
interface GbDb 
{ 
    function Select(); 
    function Insert($obj); 
} 
class GuestBookDb implements GbDb 
{ 
    public function Select() 
    { 
        // реализация 
    } 
    public function Insert($obj) 
    { 
        // реализация 
    } 
} 
class SgbDb extends GuestBookDb 
{ 
    public function Select() 
    { 
        // реализация 
    } 
    public function Insert($obj) 
    { 
        // реализация 
    } 
} 
?>
Мы можем наследовать сразу несколько интерфейсов. Усовершенствуем нашу гостевую, добавим методы обновления базы данных и выборки определенной записи по конкретному идентификационному номеру.
<? 
interface GbDb 
{ 
    function Select(); 
    function Insert($obj); 
} 
interface GbDbNew 
{ 
    function Update($id, $obj); 
    function SelectWithId($id); 
} 
class GuestBookDb implements GbDb, GbDbNew 
{ 
    public function Select() 
    { 
        // реализация 
    } 
    public function Insert($obj) 
    { 
        // реализация 
    } 
    public function Update($id, $obj) 
    { 
        // реализация 
    } 
    public function SelectWithId($id) 
    { 
        // реализация 
    } 
} 
class SgbDb extends GuestBookDb 
{ 
    public function Select() 
    { 
        // реализация 
    } 
    public function Insert($obj) 
    { 
        // реализация 
    } 
    public function Update($id, $obj) 
    { 
        // реализация 
    } 
    public function SelectWithId($id) 
    { 
        // реализация 
    } 
} 
?>
Мы можем наследовать одновременно как класс, так и интерфейс. При этом класс наследуется первым. Один интерфейс может наследовать другой интерфейс применяя ключевое слово extends.
Вот мы и рассмотрели некоторые особенности объектно-ориентированного подхода написания сценариев на языке РНР. Мы не затронули некоторые темы, например, статические элементы класса, обработку исключений. Когда вы научитесь проектировать классы и писать ОО код, то и эти темы будут вам более понятны.
Введение
Данная статья рассчитана на начинающих разработчиков в области ООП. Я работаю с пятой версией РНР, поэтому и статья рассчитана на эту версию.
Первое, что необходимо понимать - класс это не набор функций или удобный контейнер для переменных, а абстрактный тип данных (АТД). Язык РНР не является строго типизированным языком, поэтому для начала необходимо разобраться с "простыми" типами. Целые числа (1, 45, 100, 378 и т.д.) имеют целочисленный тип, integer. Массивы – тоже тип данных. Более подробно с типами данных можно ознакомиться в документации - http://www.php.net/manual/ru/language.types.php. Класс также является типом данных, а объект - своеобразная переменная этого типа.
При создании класса чётко понять задачу, которую мы хотим представить. Часто построение класса является моделированием той сущности, которую необходимо перенести в код. Объект является отражением сущности, которая описана в виде класса. При моделировании класса стоит выявить те необходимые части сущности, над которыми будут производиться необходимые действия, с помощью методов. То есть необходимые части сущности являются полями класса, они как раз отражают данные, которые составляют общий тип данных. Этим тип объекта напоминает данные типа массив.
Методы выполняют разнообразные действия над данными. Методы следует проектировать так, чтобы они работали только с теми данными, которые определены в классе. Не рекомендуется определять методы, которые как-либо влияют на внешние данные. Так называемая стратегия слабого связывания - чем меньше связей между классами и внешними данными - тем проще извлечь класс из системы и повторно использовать его вновь.
Например, класс пачки сигарет состоит из упаковки и массива самих сигарет, следует создавать методы, которые работают только с упаковкой и сигаретами. Нет необходимости использовать методы, с помощью которых мы открываем бутылку газировки или даже прикуриваем сигарету. Каждый класс должен реализовывать только те действия, которые работают с данными самого класса. Не надо пытаться засунуть в один класс всю реализацию целого приложения. Например, есть сайт, который состоит из главной страницы, гостевой книги, страницы новостей, раздела статей и ссылок на дружественные сайты. Опишите каждый раздел сайта своим классом или классами. Классы, отражающие разделы сайта, могут вступать в наследование друг с другом. Т.е. мы представляем раздел как сущность, отдельный тип данных. Снова повторю, что в классе, моделирующем раздел новостей, не надо определять элементы других разделов, если нет необходимости. Если требуется что-то представить из других разделов, используйте включение. Например, пусть на странице новостей требуется вывести заголовки последних статей. Определите в классе новостей поле типа класса статей и используйте методы этого типа. Не надо пытаться писать методы, выводящие заголовки статей в классе, моделирующем новости. Определите необходимый метод в соответствующем классе, классе, представляющем статьи.
Теперь приступим к конкретным примерам. Попробуем создать простейшую гостевую книгу.
Первое, что сделаем, это определим части необходимой нам сущности. Этими частями будут имя посетителя, его email и собственно его сообщение. Приступим.
<? 
class GuestBook 
{ 
    private $name; 
    private $email; 
    private $msg; 
     
    public function __construct($name, $email, $msg) 
    { 
        $this->name = $name; 
        $this->email = $email; 
        $this->msg = $msg; 
    } 
} 
?>
Во-первых, мы объявили необходимые части нашего типа данных. Во-вторых, поля объявлены с модификатором доступ private, который означает, что с полями класса можно работать только методам этого же класса, но про модификаторы доступа будет рассказано позже. В-третьих, мы объявили метод __construct($name, $email, $msg). Этот метод является конструктором класса. О конструкторах и деструкторах будет рассказано немного позже, сейчас же запомните, что конструкторы всегда автоматически выполняются первыми, до всех остальных методов класса, непосредственно сразу после выделения памяти под объект. Деструкторы же выполняются последними, непосредственно перед уничтожением объекта.
В-четвёртых, мы использовали ключевое слово $this. $this означает ссылку на объект, для которого реализована та или иная конструкция. В отличие от других языков программирования, при обращении к членам класса, обязательно следует использовать $this: $this->name и $name - это две разные переменные.
Теперь подумаем над тем, как нам обращаться к полям объекта. Реализуем методы, которые вернут соответствующие значения.
<? 
class GuestBook 
{ 
    private $name; 
    private $email; 
    private $msg; 
     
    public function __construct($name, $email, $msg) 
    { 
        $this->name = $name; 
        $this->email = $email; 
        $this->msg = $msg; 
    } 
     
    public function getName() 
    { 
        return $this->name; 
    } 
    public function getEmail() 
    { 
        return $this->email; 
    } 
    public function getMsg() 
    { 
        return $this->msg; 
    } 
} 
?>
Теперь данные класса можно читать из вне.
При разработке гостевой книги неизбежно встаёт вопрос, как хранить данные. Воспользуемся для этого базой данных MySQL. Добавим новое поле типа DataBase – это специальный тип для работы с БД. В конструкторе проведем инициализацию. Создадим методы для получения данных из базы и наполнения базы. Метод чтения (SELECT) реализуем так:
<?php 
public function Select() 
{ 
    $sql = "SELECT name, email, msg FROM guestbook"; 
    $dbArray = $this->db->Db2Array(); 
    foreach($dbArray as $rows) 
    { 
        $outPut[] = new GuestBook($rows['name'], $rows['email'], $rows['msg'], $this->db); 
    } 
    return $outPut; 
} 
?>
Метод добавления значений реализуем следующим образом:
<?php 
public function Insert() 
{ 
    $sql = "INSERT INTO guestbook (name, email, msg) VALUES('{$this->name}', '{$this->email}', '{$this->msg}')"; 
    if($this->db->Insert($sql) === TRUE) 
        return TRUE; 
    return FALSE; 
} 
?>
Первый метод возвращает массив объектов GuestBook, второй возвращает TRUE, если данные успешно добавлены, и FALSE - в противном случае.
В первой строке метода Select() cоздается SQL-запрос к базе на выборку всех значений.
Во второй строке из базы извлекается результирующая таблица в виде массива $dbArray. Как это происходит будет описано позже. В пятой строке мы создаем массив объектов типа GuestBook, который и возвращается в качестве результата. Напомню, чтобы обратиться к соответствующим полям мы реализовали соответствующие методы.
Для второго метода также создан SQL-запрос, но уже на добавление данных в базу (INSERT). Если запрос выполнен удачно, возвращаем истину (TRUE), иначе – ложь (FALSE).
Разрабатывая Web-приложение с использованием ООП, следует помнить о производительности, которая всегда ниже по сравнению с процедурным подходом. Метод Select, возвращающий значения каждый раз требует обращения к новому объекту типа DataBase. Если в базе хранится тысячи записей, это вызовет значительные накладные расходы - обработка каждой из записей потребует создания дополнительного объекта DataBase. Давайте придумаем выходы из подобной ситуации. Первое что приходит на ум, это не инициализировать поле типа DataBase, а написать соответствующий метод, который инициализирует поле $db.
<?php 
public function setDb($db) 
{ 
    $this->db = $db; 
} 
?>
Напомним, что параметр $db – это экземпляр класса DataBase. Можно поступить и так:
<?php 
public function setDb($db) 
{ 
    $this->db = new DataBase(‘login’,’password’,’host’,’database’); // Параметры – значения подключения к базе данных. 
} 
?>
Однако снова таки тысячу раз инициализировать объект типа DataBase не оптимально. Надо думать дальше smile
Практика показала, что более оптимально разбить подобный класс на два: в первом реализовать инициализацию сущности, а во второй объявить необходимые методы для работы с базой данных.
<? 
class GuestBook 
{ 
    private $name; 
    private $email; 
    private $msg; 
     
    public function __construct($name, $email, $msg) 
    { 
        $this->name = $name; 
        $this->email = $email; 
        $this->msg = $msg; 
    } 
     
    public function getName() 
    { 
        return $this->name; 
    } 
    public function getEmail() 
    { 
        return $this->email; 
    } 
    public function getMsg() 
    { 
        return $this->msg; 
    } 
} 
class GuestBookDb 
{ 
    private $db; 
     
    public function __construct($db) 
    { 
        $this->db = $db; 
    } 
    public function Select() 
    { 
        $sql = "SELECT name, email, msg FROM guestbook"; 
        $dbArray = $this->db->Db2Array(); 
        foreach($dbArray as $rows) 
        { 
            $outPut[] = new GuestBook($rows['name'], $rows['email'], $rows['msg']); 
        } 
        return $outPut; 
    } 
    public function Insert($obj) 
    { 
        $name = $obj->getName(); 
        $email = $obj->getEmail(); 
        $msg = $obj->getMsg(); 
         
        $sql = "INSERT INTO guestbook (name, email, msg) VALUES('$name', '$email', '$msg')"; 
        if($this->db->Insert($sql) === TRUE) 
            return TRUE; 
        return FALSE; 
    } 
} 
?>
Что же изменилось: во-первых, мы получаем также массив объектов с помощью метода Select, но теперь он содержит только необходимые данные. Теперь на тысячу объектов GuestBook, создается только один объект GuestBookDb. В памяти не будет храниться тысячу раз данные типа DataBase. Во-вторых, немного изменился метод Insert(). У него появился параметр – объект типа GuestBook – это необходимо, чтобы занести данные в базу. В результате таких преобразований нагрузка на сервер немного снизится.
E-mail
Swift Mailer
Swift Mailer легко интегрируется в любое PHP-приложение. Это гибкий и элегантный ООП-подход к отправке писем с множеством функций: отправка эл.почты, используя SMTP, SendMail, Postfix, поддержка серверов и др.
PHPMailer
Лучший класс для работы с эл. почтой. Поддерживает сообщения в цифровой форме, S/MIME шифрование, текстовые и HTML-письма, изображения, поддерживает несколько E-mail’ов, SMTP-аутентификация.
Формы
Securimage PHP Captcha
Скрипт для создания сложных изображений (капч) для защиты от спамеров. Легко добавляется в любую форму.
phpObjectForms
ООП библиотека для создания и обработки HTML-форм. Основные возможности: поддержка всех стандартных форм ввода, проверка на стороне сервера с помощью регулярных выражений, проверка на стороне клиента с помощью Javascript, поддерживает шаблоны форм. Стили форм записываются в CSS и вы можете легко настроить их отображение.
Изображения / медиа / файлы
PHP Thumb
Маленькая библиотека для работы с изображениями: изменение размеров, поворот, Crop. Вы также можете добавлять пользовательские функции. Может выполнять подряд несколько действий, без неодходимости сохранять и повторно инициализировать класс со всеми манипуляциями.
WideImage
ООП-библиотека для работы с изображениями. Она обеспечивает простой способ подгрузки и хранения изображений из файлов, баз данных и URL. Поддерживает наиболее распространённые графические форматы: GIF, PNG, JPEG, GD и GD2.
Smart Image Resizer
Позволяет изменять размер и кропать любые изображения на вашем сайте фактически их не касаясь. Просто загрузите изображения в максимальном его размере а затем используйте любые его части. Обладает множеством полезных функций.
class.upload.php
Это небольшой PHP-скрипт для закачки изображений и управления ими на сервере. Он может конвертировать изображения из одного формата в другой, изменять размер, добавлять метки, водяные знаки и “размывать” изображения. Вы можете использовать его для файлов, загруженных с помошью HTML-формы, Flash Uploader или локальных файлов.
getID3()
PHP-скрипт, который извлекает полезную информацию из MP3 и других мультимедийных форматов (OGG, WMA, WMV, ASF, WAV, AVI, AAC, VQF, FLAC, MusePack, Real, QuickTime, Monkey’s аудио, MIDI и т.д.)
Javascript / AJAX
PHPLiveX
Небольшая библиотека, которая позволяет легко интегрировать технологию AJAX в ваш веб-проект. Вы можете отправлять данные формы и отправлять запрос на другую страницу без перезагрузки текущей.
Xajax
Очень известная библиотека и Javascript-движок, который позволяет вам легко создавать мощные Ajax-приложения с использованием HTML, CSS, Javascript, PHP. Страницы получают возможность асинхронно посылать запросы на сервер и обновлять содержимое страницы без её перезагрузки.
RSS/Atom
SimplePie
PHP-класс, который предоставляет простую API для выполнения всей грязной работы по получению, кэшированию, разбору и нормализации структуры RSS и Atom форматов.
Безопасность
PHP Intrusion Detection System (PHPIDS)
Пресекает попытки злоумышленников нарушить работу вашего сайта. В настоящее время PHPIDS обнаруживает всевозможные XSS, SQL-инъекции, RFE / LFI, DoS и LDAP нападений.
Тестирование и отладка
PHPUnit
SimpleTest
PHP Debug
Базы данных
ADOdb
Объектно-ориентированная библиотека. Сделана по побразцу Microsoft ADO, но имеет ряд усовершенствований, которые делают её уникальной (например сводные таблицы, кэширование записей…) Поддерживает большое количество баз данных, в том числе: MySQL, PostgreSQL, Interbase, Firebird, Informix, Oracle, MS SQL, FoxPro, Access, ADO, Sybase, FrontBase, DB2, SAP DB, SQLite, Netezza, LDAP.
Doctrine
Представляет собой объектно-реляционное отображение (ORM) в PHP 5.2.3+. Ключевая особенность этой библиотеки в том, что она позволяет писать запросы к базе данных в объектно-ориентированном виде, с помощью собственного диалекта SQL – Doctrine Query Language (DQL). Это мощная альтернатива обычным SQL-запросам.
PHPLINQ
Набор PHP-классов для управления базами данных.
Работа с документами
TCPDF
Класс, генерирующий PDF документы. Не требует других библиотек, поддерживает форматы ISO, в т.ч. UTF-8, Unicode, RTL и HTML.
PHPPowerPoint
Работает на основе стандартов Microsoft OpenXML. Позволяет читать и записывать документы PowerPoint. Дает возможность управлять мета-данными (автор, название, описание, …), добавлять слайды и изображения в презентации и многое другое.
PHPExcel
Также работает на Microsoft OpenXML. Позволяет читать и записывать файлы Excel. Возможности включают в себя: редактирование мета данных (автор, название, описание, …), управление электронными таблицами, шрифтами, стилями, добавление изображений и многое другое.
PhpRtf Lite
Позволяет создавать и редактировать, совместимые с MS Word и Open Office Writer, RTF с помощью PHP. Позволяет контролировать практически всё. Совместим с кодировкой UTF-8
Облако тегов / Авторы