Перегрузка
Перегрузка в PHP означает возможность динамически » создавать » свойства и методы. Эти динамические сущности обрабатываются с помощью «волшебных» методов, которые можно создать в классе для различных видов действий.
Методы перегрузки вызываются при взаимодействии с теми свойствами или методами, которые не были объявлены или не видны в текущей области видимости. Далее в этом разделе мы будем использовать термины » недоступные свойства » или » недоступные методы » для отражения этой комбинации объявления и области видимости.
Все методы перегрузки должны быть объявлены как public.
Замечание:
Ни один аргумент не может быть передан по ссылке в эти «волшебные» методы.
Замечание:
Интерпретация » перегрузки » в PHP отличается от остальных объектно-ориентированных языков. Традиционно перегрузка означает возможность иметь множество одноименных методов с разным количеством или различными типами аргументов.
Список изменений
Версия | Описание |
---|---|
5.3.0 | Добавлен метод __callStatic(). Добавлено предупреждение об усилении публичной видимости и не-статичном объявлении. |
5.1.0 | Добавлены методы __isset() и __unset(). Добавлена поддержка для перегрузки приватных свойств с помощью __get(). |
5.0.0 | Добавлен метод __get(). |
Перегрузка свойств
Метод __set() будет выполнен при записи данных в недоступные свойства.
Метод __get() будет выполнен при чтении данных из недоступных свойств.
Метод __isset() будет выполнен при использовании isset() или empty() на недоступных свойствах.
Метод __unset() будет выполнен при вызове unset() на недоступном свойстве.
Аргумент $name представляет собой имя вызываемого свойства. Метод __set() содержит аргумент $value , представляющий собой значение, которое будет записано в свойство с именем $name .
Перегрузка свойств работает только в контексте объекта. Данные магические методы не будут вызваны в статическом контексте. Поэтому данные методы не должны объявляться статичными. Начиная с версии PHP 5.3.0, при объявлении «волшебного» метода в качестве static будет показано предупреждение.
Замечание:
Возвращаемое значение метода __set() будет проигнорировано из-за способа обработки в PHP оператора присваивания. Аналогично, __get() никогда не вызовется при цепных присваиваниях, например, таких:
Пример #1 Перегрузка свойств с помощью методов __get(), __set(), __isset() и __unset()
class PropertyTest
/** Место хранения перегружаемых данных. */
private $data = array();
?php
/** Перегрузка не применяется к объявленным свойствам. */
public $declared = 1 ;
/** Здесь перегрузка будет использована только при доступе вне класса. */
private $hidden = 2 ;
public function __set ( $name , $value )
echo «Установка ‘ $name ‘ в ‘ $value ‘\n» ;
$this -> data [ $name ] = $value ;
>
public function __get ( $name )
echo «Получение ‘ $name ‘\n» ;
if ( array_key_exists ( $name , $this -> data )) return $this -> data [ $name ];
>
$trace = debug_backtrace ();
trigger_error (
‘Неопределенное свойство в __get(): ‘ . $name .
‘ в файле ‘ . $trace [ 0 ][ ‘file’ ] .
‘ на строке ‘ . $trace [ 0 ][ ‘line’ ],
E_USER_NOTICE );
return null ;
>
/** Начиная с версии PHP 5.1.0 */
public function __isset ( $name )
echo «Установлено ли ‘ $name ‘?\n» ;
return isset( $this -> data [ $name ]);
>
/** Начиная с версии PHP 5.1.0 */
public function __unset ( $name )
echo «Уничтожение ‘ $name ‘\n» ;
unset( $this -> data [ $name ]);
>
/** Не «волшебный» метод, просто для примера. */
public function getHidden ()
return $this -> hidden ;
>
>
$obj -> a = 1 ;
echo $obj -> a . «\n\n» ;
var_dump (isset( $obj -> a ));
unset( $obj -> a );
var_dump (isset( $obj -> a ));
echo «\n» ;
echo «Давайте поэкспериментируем с private свойством ‘hidden’:\n» ;
echo «Private свойства видны внутри класса, поэтому __get() не используется. \n» ;
echo $obj -> getHidden () . «\n» ;
echo «Private свойства не видны вне класса, поэтому __get() используется. \n» ;
echo $obj -> hidden . «\n» ;
?>
Результат выполнения данного примера:
Установка 'a' в '1' Получение 'a' 1 Установлено ли 'a'? bool(true) Уничтожение 'a' Установлено ли 'a'? bool(false) 1 Давайте поэкспериментируем с private свойством 'hidden': Private свойства видны внутри класса, поэтому __get() не используется. 2 Private свойства не видны вне класса, поэтому __get() используется. Получение 'hidden' Notice: Неопределенное свойство в __get(): hidden в файле на строке 70 в на строке 29
Перегрузка методов
В контексте объекта при вызове недоступных методов вызывается метод __call().
В статическом контексте при вызове недоступных методов вызывается метод __callStatic().
Аргумент $name представляет собой имя вызываемого метода. Аргумент $arguments представляет собой числовой массив, содержащий параметры, переданные в вызываемый метод $name .
Пример #2 Перегрузка методов с помощью методов __call() и __callStatic()
class MethodTest public function __call ( $name , $arguments ) // Замечание: значение $name регистрозависимо.
echo «Вызов метода ‘ $name ‘ »
. implode ( ‘, ‘ , $arguments ). «\n» ;
>
?php
/** Начиная с версии PHP 5.3.0 */
public static function __callStatic ( $name , $arguments ) // Замечание: значение $name регистрозависимо.
echo «Вызов статического метода ‘ $name ‘ »
. implode ( ‘, ‘ , $arguments ). «\n» ;
>
>
$obj = new MethodTest ;
$obj -> runTest ( ‘в контексте объекта’ );
MethodTest :: runTest ( ‘в статическом контексте’ ); // Начиная с версии PHP 5.3.0
?>
Результат выполнения данного примера:
Вызов метода 'runTest' в контексте объекта Вызов статического метода 'runTest' в статическом контексте
Перегрузка методов и аксессоры в PHP
С помощью перегрузки методов в PHP, можно создать метод с таким же названием, что и в базовом классе, который заменит метод базового класса при вызове.
Все методы перегрузки объявляются исключительно только как «public».
class base_class < public function method_base() < echo 'Вызван метод "method_base" базового класса'; >> class derivative_class extends base_class < public function method_base() < echo 'Вызван метод "method_base" производного класса'; >> $_new_class = new derivative_class(); $_new_class->method_base();
Результатом работы будет вывод фразы «Вызван метод «method_base» производного класса».
Таким образом, метод base_class::method_base() не вызывается при обращении к объекту производного класса «derivative_class».
Однако в рамках производного класса остаётся возможность вызвать метод базового класса, обратившись к нему при помощи префикса «parent::».
Ниже будет представлен пример перегрузки метода «method_base()», при котором метод производного класса сначала вызывает метод базового класса.
class base_class < public function method_base() < echo 'Вызван метод "method_base" базового класса'; >> class derivative_class extends base_class < public function method_base() < parent::method_base(); echo '
Вызван метод "method_base" производного класса'; > > $_new_class = new derivative_class(); $_new_class->method_base();
Результат покажет уже две строчки «Вызван метод «method_base» базового класса» и «Вызван метод «method_base» производного класса».
Таким образом, при помощи перегрузки метода можно как расширить метод базового класса, так и полностью заменить его уже новым методом.
Ниже будет продемонстрирован пример перегрузки методов с класса «mysqli»:
class connect extends mysqli < public function __construct($_host_base = null, $_user_base = null, $_password_base = null, $_data_base = null) < parent::init(); # Перегружаем метод инициализации, чтобы установить опции; /* - Перегружаем опцию (метод) и устанавливаем таймаут соединения; */ if (!parent::options(MYSQLI_OPT_CONNECT_TIMEOUT, 5)) < die('Error: # Превышен таймаут соединения'); >/* - Прегружаем метод соединения с базой данных, с перегрузкой опции (метода) для установления кодировки ; */ if (!parent::real_connect($_host_base, $_user_base, $_password_base, $_data_base, parent::options(MYSQLI_INIT_COMMAND, 'SET NAMES utf8'))) < die('Connect DataBase Error: #'. mysqli_connect_error() .' : ('. mysqli_connect_errno() .')'); >> // Далее мы создадим свой метод в котором перегрузим метод из базового класса; public function sqlQuery($_Query) < return parent::query($_Query); >> $_Sql = new connect('localhost', 'Юзер', 'Пароль', 'База') or die('Error Class: connect'); $_Sql->sqlQuery('Запрос');
В примере конструктор имеет нужные нам опции, при этом из его тела происходит перегрузка методов из базового класса с помощью ключевого слова , после чего выполняются действия, специфические для класса «mysqli». Аналогичным образом могут перегружаться любые методы базового класса в производном классе «connect».
Помимо этого в PHP существует ещё два «магических» метода (аксессоры),при помощи которых можно перегрузить свойства, которые вызываются при обращении к свойству класса и при изменении его значения, — это методы «__get()» и «__set()».
Данные методы «__get()» и «__set()» позволяют легко проводить динамическое назначение свойств объектам, а в качестве параметров этим методам передаются имена свойств.
А также, метод «__set()» может получить и значение, которое присваивается указанному свойству. Приведём пример:
class setget < // Член в котором будут храниться перегружамые данные; private $_array = array() ; /* - Выполняем запись данных в недоступные свойства */ private function __set($_Home, $_Value) < $this->_array[$_Home] = $_Value; > /* - Выполняем чтение данных из недоступных свойств */ private function __get($_Home) < return $this->_array[$_Home]; > > $class_ = new setget(); $class_->test = 'Обращение к несуществующему элементу'; echo $class_->test;
Как видно из примера, класс «setget» перехватывает обращения к членам и создает соответствующий элемент в массиве $_array.
При попытке присвоить члену значение, создаётся новый элемент в массиве $_array.
Если член в классе уже существует, то аксессоры «__set()» и «__get()» перехватывают обращение к нему, если он имеет спецификатор доступа «private» и не перехватывают, если он имеет спецификатор «public».
Ещё существуют новый метод перегрузки «__callStatic()» который добавлен в PHP 5.3, и метод «__call». Пример:
class callandstatic < // Общее замечание: Значение $_Home регистрозависимо; public function __call($_Home, $_arg) < echo implode(', ', $_arg) . $_Home; >public static function __callStatic($_Home, $_arg) < echo implode(', ', $_arg) . $_Home; >> $class_ = new callandstatic; $class_->_test('Вызов недоступного метода '); callandstatic::_test('Вызов недоступного статического метода '); /* * Начиная только с версии PHP 5.3 иначе будет вызвана ошибка * (Fatal error: Call to undefined method callandstatic::_test()) */
$_Home содержит имя метода, а $_arg массив в котором храняться параметры которые передаются из метода $_Home.
Комментировать
Однако перегружать методы можно только в процессе наследования. В одном и том же классе нельзя записать методы с одинаковыми названиями, но с разными аргументами, так как сигнатура метода не зависит от аргументов. Поддержка полиморфизма в PHP неполная.
Я извиняюсь, что придираюсь к терминологии, но полагаю, что в статье подразумевается переопределение (override), а не перегрузка (overload). Последнее подразумевает выбор одного из методов с одним и тем же названием, в зависимости от сигнатуры метода, а первое — реализацию в классе метода с такой же сигнатурой, как и в классе родителя, и выбор во время выполнения программы нужной реализации, в зависимости от конкретного типа объекта.
Перегрузка методов в php?
Я учился программировать сначала на Java, затем подвернулась работа PHP программиста и я достаточно быстро на него пересел. Однако насколько вы можете знаеть Java — язык строгой типизации, а PHP нет, отсюда у меня появились некоторые проблемы с написанием некотрых классов.
Например в PHP нет явной перегрузки методов, я предлогаю обсудить как можно найти выход из такой сютуации.
Для примера рассмотрим класс продуктов допустим в интернет магазине.
class Prodcuts < public $name; public $price; public $id; function __construct($id)< $sql = "SELECT * FROM products WHERE = mysql_query($sql); $array = mysql_fetch_array($result); $this->name = $array['name']; $this->price = $array['price']; $this->id = $id; > >
Создать новый обьект в таком сулучае можно будет так:
$objProd = new Product(1)
В конструкторе класса выбиратся всё что связанно с и обьект инициализируется.
Но в какой то момент я понял мне требуется инициализация обьекта и по другим параметрам,
нариример для такого метода:
static function create($name,$price) < $sql = "INSERT INTO poducts(name,price) VALUES ('$name','$price')"; if(mysql_query($sql))< return new Prodcuts($name,$price); >>
Тут родился вопрос перегрузки конструктора или какого то альтернативного способа инициализации обьекта.
Я знаю несколько способов и поделюсь ими свами. Буду рад если кто-то предложит ещё варианты решения этой задачи.
Cпособ №1. Использование опциональных параметров.
Переделам конструктор так:
function __construct($id=null,$name=null,$price=null)< if ( isset($name) && isset($price))< $this->name = $name; $this->price = $price; >else < $sql = "SELECT * FROM products WHERE = mysql_query($sql); $array = mysql_fetch_array($result); $this->name = $array['name']; $this->price = $array['price']; $this->id = $id; > >
тоесть мы можем не указывать параметр id
получается так:
$product = new Prodcuts(null,$name,$price);
согласитесь, это не очень красиво.
Cпособ №2. Использование функций func_num_args() и func_get_arg().
function __construct($id)< if(func_num_args()>1)< $this->name = func_get_arg(0); $this->price = func_get_arg(1); >else< $sql = "SELECT * FROM products WHERE = mysql_query($sql); $array = mysql_fetch_array($result); $this->name = $array['name']; $this->price = $array['price']; $this->id = $id; > >
Здесь мы используем две «магические» функции:
func_num_args() — возвращает колличество аргументов текущей функции,
func_get_arg() — возвращает указанный аргумент текущей функции(отсчет ведётся с ноля),
проверяем колличество указанных параметров и инициализируем объект так как нам нужно.
Способ №3. Использования для инициализации статичной функции отказ от «перегрузки» конструкторов
static public function createWithTwoParams($name,$price) < $object = new self(); $object->initTwoParams($name, $price); return $object; > protected function initTwoParams($price,$name) < $this->name = $name; $this->price = $price; >
В таком случае получается:
статичную функцию можно выполнить без создания объекта, и она вернёт нам экземпляр класса product
$product = Products::createWithTwoParams($name,$price);
Но и конструктор мы тоже сможем использовать.
$product = new Prodcuts($id);
Пусть в PHP нет перегрузки и ещё чего то, зато нетсрогая типизация позваляет вытворять то что «взрослым языкам» и не снилось, да и ктому же это очень удобно, а все проблемы с вязанные с недостатком чего-либо можно решить.
PS: мой первый пост, не судите строго.