Магические методы в ООП PHP

Магические методы



Магические методы: php.net


Метод __toString()

Методы __get() и __set()



Магические методы - это методы, которые вызываются не явно, то есть они вызываются автоматически,

например: методы __construct() и __destruct().



Метод __toString()




__toString() - этот метод позволяет классу решать как он будет реагировать при преобразовании в строку.


Есть объект класса BookProduct, внутри этого класса есть метод getProduct(), который выводит информацию о продукте, выведем его с помощью echo, так как там - return $out:


echo $book -> getProduct();


Если-же мы попытаемся вывести: echo $book;, то браузер выведет ошибку: "объект $book не может быть преобразован в строку". Операторы echo, print выводят строку, а $book - объект!.


Однако, мы можем использовать метод __toString(), который будет решать, что делать с объектом, который мы пытаемся преобразовать в строку. Объявим такой метод в классе BookProduct:


...
public function __toString()
{
// echo 'Hello <br>'; // проверяем, что метод работает
return 'Hello <br>';
// - можно сразу вернуть
// - теперь в браузере выводится "Hello", и ошибки нет,
// то есть отрабатывает метод __toString()

return $this -> getProduct();
// - возвращаем результат работы метода getProduct()
}
...



- когда мы пытаемся работать с объектом, как со строкой (вывести его, как строку), тогда PHP ищет магический метод __toString() и выполняет его. Мы можем вернуть пустое значение: return 'Hello <br>'; и ничего не произойдет, а можно сделать и что-нибудь полезное, например, вернуть результат работы метода getProduct() И у нас получается, что запись echo $book; является полным аналогом записи echo $book -> getProduct();


Файл BookProduct.php:


-- файл BookProduct.php --

<?php
namespace app;

use core\interfaces\I3D;
use core\Product;

class BookProduct extends Product implements I3D
{
public $numPages;

public $action1;
public $action2;

public function __construct($name, $price, $numPages)
{
parent :: __construct($name, $price);
$this -> numPages = $numPages;
$this -> setDiscount(5);
}

// объявляем магический метод __toString()
public function __toString()
{
//echo 'Hello <br>';
//return 'Hello <br>';
// - можно сразу вернуть
// теперь в браузере выводится "Hello", и ошибки нет,
// то есть отрабатывает метод __toString()

return $this -> getProduct();
// - возвращаем результат работы метода getProduct()
}

public function test()
{
}

public function getProduct()
{
$out = parent :: getProduct();
$out .= "Цена без скидки: {$this -> price}<br>";
$out .= "Кол-во страниц:: {$this -> cpu}<br>";
$out .= "Скидка: {$this -> getDiscount()}%<br>";
return $out;
}

public function getNumPeges()
{
return $this -> numPages;
}

public function addProduct($name, $price, $numePage=0)
{
// --------------------------
var_dump($name);
var_dump($price);
var_dump($numePage);
}

}
?>





Файл index.php:


-- файл index.php --

<?php
use app\{BookProduct,NotebookProduct};
use core\interfaces\{IGadget,I3D};

error_reporting(-1);

require_once __DIR__ . '/vendor/autoload.php';

function debug($data)
{
echo '<pre>' . print_r($data, 1 ) . '</pre>';
}

function offerCase($product)
{
echo "<p>Предлагаем чехол для гаджета {$product -> getName()} </p>";
}

$book = new BookProduct('Три мушкетера', 20, 1000);
$notebook = new NotebookProduct('Dell', 1000, 'Intel' );

// Метод __toString() ---------------

// Есть объект класса BookProduct, внутри этого класа есть метод getProduct(),
// который выводит информацию о продукте;
// выведем его (с помощью echo, так как там - return $out;)

echo $book -> getProduct();

// Если мы попытаемся работать с объектом как со строкой, то будет вызван
// магический метод __toString(), который в свою очередь вызывает
// метод getProduct.
// И получаем, что запись echo $book - является полным аналогом
// записи echo $book->getProduct

echo $book;

?>
Получим:

Результат работы echo $book->getProduct():
О товаре:
Наименование: Три мушкетера
Цена со скидкой: 19
Цена без скидки: 20
Кол-во страниц: 1000
Скидка: 5%

Результат работы echo $book:
О товаре:
Наименование: Три мушкетера
Цена со скидкой: 19
Цена без скидки: 20
Кол-во страниц: 1000
Скидка: 5%





Методы __get() и __set()




__get() и __set() - это аналоги гетеров и сетеров, только вызываются они "магически".

Эти методы предназначены для работы со свойствами, которые не были объявленны в классе или родителе класса.

Метод __get() вызывается, когда мы обращаемся к неопределенному свойству.

Метод __set() вызывается, когда мы пытаемся присвоить что-то неопределенному свойству.


Вызовем в индексном файле тестовое свойство: $book->test = 'Hello' - в браузере оно появляется, хотя в классе это свойство нами не было предусмотрено, что не всегда хорошо, поскольку мы можем случайно обратиться к этому свойству.

Что-бы этого не происходило, можно использовать "магические методы" __get() и __set().

В классе BookProduct объявим эти методы:


__get() - этот метод принимает в себя один параметр - это имя свойства ($name), которое мы хотим получить (такого свойства нет):


public function __get($name)
{
var_dump($name);
}




__set() - этод метод имеет два параметра: имя неопределенного свойства ($name), к которому мы обращаемся, и его значение ($value):


public function __set($name, $value)
{
var_dump($name, $value);
return ;
}



После обновления странички в браузере, мы видим, что свойство "Hello" - пропало, оно уже не было динамически создано для объекта, но отработал метод __set() и метод __get().


выведет:
string 'test' (length=4)
- имя неопределенного свойства (работа метода __set($name, $value))

string 'HELLO' (length=5)
- его значение (работа метода __set($name, $value))

string 'test' (length=4)
- имя неопределенного свойства, к которому мы пытаемся обратиться (работа метода __get($name))



Эти методы можно использовать по желанию: можно ничего не делать (return), можно логировать какую-нибудь ошибку, можно использовать патерн программирования, например - реестр, и записать какое-то свойство в массив и затем с ним работать и т. д.


Например, для метода __get():


В классе Product есть метод getName(), при этом свойство $name является приватным (private $name).


Файл Product.php:


-- файл Product.php --

<?php

namespace core;

abstract class Product
{
private $name; // приватное свойство $name
protected $price ;
private $discount = 0;

...
...
...

// метод getName()
public function getName()
{
return $this -> name;
}

...
...
...

}
?>




- попробуем к нему обратиться (в индексном файле):

echo $book->name;


При этом отрабатывает метод __get()(в классе BookProduct) и в браузере получаем: string(4) "name".

Если закоментировать метод __get(), то в браузере отобразится ошибка.


В методе __get() (в классе BookProduct) создадим переменную:

$method = "get{$name}"


- все наши гетеры начинаются на get, а в переменную $name параметром - приходит имя того свойства, к которому мы хотим обратиться, в данном случае - приходит name.

Выведем ее в браузере, получим - getname.


Первую букву приводим к верхнему регистру:

$name = ucfirst($name);


Проверяем, есть ли у нас такой метод программно:

if(method_exists($this, $method)),


- первым параметром эта функция принимает объект того класса, которого мы хотим проверить,

- на этот объект ссылается псевдопеременная $this;

- вторым параметром - наименование метода, который хранится у нас в переменной $method.


То есть, функция method_exists($this, $method) проверяет: есть-ли метод $method у объекта $this.

- Если есть - возвращаем результат работы этого метода: return $this -> $method();

- А если нет - тогда мы ничего не делаем.


В результате в браузере получим - "Три мушкетера".


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


Файл BookProduct.php:


-- файл BookProduct.php --

<?php
namespace app;

use core\interfaces\I3D;
use core\Product;

class BookProduct extends Product implements I3D
{
public $numPages;

public $action1;
public $action2;

public function __construct($name, $price, $numPages)
{
parent :: __construct($name, $price);
$this -> numPages = $numPages;
$this -> setDiscount(5);
}

// объявляем магический метод __toString()
public function __toString()
{
return $this -> getProduct();
// возвращаем результат работы метода getProduct()
}

// объявляем магический метод __get()
// - этот метод принимает в себя один параметр - это имя свойства ($name),
// которое мы хотим получить.
public function __get($name)
{
var_dump($name);
//echo $method = "get{$name}"; // выведет getname

// проверка наличия метода $method и его вызов при обращении к
// неопределенному свойству
$name = ucfirst($name);
// - приводим первую букву к верхнему регистру (n -> N)
$method = "get{$name}"; // создадим переменную

// проверяем, есть ли у нас такой метод программно
if(method_exists($this, $method))
// Первым параметром эта функция принимает объект того класса, которого
// мы хотим проверить. На этот объект ссылается псевдопеременная $this.
// Вторым параметром - наименование метода, который хранится у нас
// в переменной $method.

// То есть, функция method_exists($this, $method) проверяет:
// есть-ли метод $method у объекта $this.
{
// если есть - возвращаем результат работы этого метода
return $this -> $method();
}
}

// объявляем магический метод __set()
// - этод метод имеет два параметра: имя неопределенного свойства ($name),
// к которому мы обращаемся, и его значение ($value)
public function __set($name, $value)
{
var_dump($name, $value);
return;
}

public function test()
{
}

public function getProduct()
{
$out = parent :: getProduct();
$out .= "Цена без скидки: {$this -> price}<br>";
$out .= "Кол-во страниц:: {$this -> cpu}<br>";
$out .= "Скидка: {$this -> getDiscount()}%<br>";
return $out;
}

public function getNumPeges()
{
return $this -> numPages;
}

public function addProduct($name, $price, $numePage=0)
{
// --------------------------
var_dump($name);
var_dump($price);
var_dump($numePage);
}

}
?>





Файл index.php:


-- файл index.php --

<?php
use app\{BookProduct,NotebookProduct};
use core\interfaces\{IGadget,I3D};

error_reporting(-1);

require_once __DIR__ . '/vendor/autoload.php';

function debug($data)
{
echo '<pre>' . print_r($data, 1 ) . '</pre>';
}

function offerCase($product)
{
echo "<p>Предлагаем чехол для гаджета {$product -> getName()} </p>";
}

$book = new BookProduct('Три мушкетера', 20, 1000);
$notebook = new NotebookProduct('Dell', 1000, 'Intel');

//offerCase($notebook);

// __get() и __set() --------------------------------------

// добавляем тестовое свойство (работа метода __set())
//$book->test='HELLO';
//debug($book);

// обращаемся к несуществующему свойству (работа метода __get())
//echo $book->test;

// обращение к приватному свойству name (работа метода __get())
echo $book -> name;

?>
Получим:
Три мушкетера





Содержание папки 16 ("Магические методы"):


16-1






Наверх Наверх