ООП PHP
Позднее статическое связывание. Цепочка методов
Позднее статическое связывание: php.net
Позднее статическое связывание
Создадим два новых класса: class A и class B:
Содержание папки 15 ("Позднее статическое связывание. Цепочка методов..."):
В классе А создадим константу TEST чтобы ее можно было наследовать и переопределить (в наследуемых классах мы можем переопределять константу в отличии от интерфейсов).
Константы принадлежат не объекту, а классу и соответственно, обращаться к ним мы можем в контексте класса, а не объекта.
- Если мы хотим работать с константой внутри класса нам необходимо ключевое слово self и двойное двоеточие "::",
- если мы хотим работать с этой константой вне класса - через имя класса и двойное двоеточие "::".
Также определим метод getTest(), для того, чтобы мы могли получить эту константу вне класса.
Файл A.php
-- файл A.php --
<?php
namespace ;
class
{
// создадим константу
const TEST = "Class A";
// определяем метод getTest() для того, чтобы получить эту константу вне класса
public function ()
{
// слово self всегда указывает на класс,
// в котором объявленна константа (TEST)
(self::TEST);
//var_dump(A::TEST);
}
}
?>
В классе В мы наследуем (extends) класс А (использовать use здесь не нужно, так как классы А и В находятся в одном пространстве имен - namespace app).
И внутри данного класса мы переопределим значение константы TEST, и укажем , что эта константа имеет значение: "Class B".
Когда мы наследуемся, то практически происходит КОПИПАСТ.
Файл B.php
-- файл B.php --
<?php
namespace ;
// В классе В мы наследуем (extends) класс А (использовать use здесь не нужно,
// так как классы А и В находятся в одном пространстве имен - namespace app).
// И внутри данного класса мы определим значение константы TEST, и
// укажем , что эта константа имеет значение: "Class B"
class extends
{
// создадим константу
const TEST = "Class B";
// Когда мы наследуемся, то практически происходит КОПИПАСТ:
public function ()
{
// слово self всегда указывает на класс,
// в котором объявленна значение константы TEST
(self::TEST);
}
}
?>
В индексном файле создадим объект класса А и объект класса В.
И вызовем метод getTest() для объектов $a и $b
Файл index.php:
-- файл index.php --
<?php
use \{ , };
use \ \{ , };
(-1);
require_once . '/vendor/autoload.php';
function ($data)
{
echo '<pre>' . ($data, 1 ) . '</pre>';
}
function ($product)
{
echo "<p>Предлагаем чехол для гаджета {$product -> ()} </p>";
}
// Создадим объект класса А и объект класса В
$a = new \ \ ();
$b = new \ \ ();
// Вызовем метод getTest() для объектов $a и $b (где используется ключевое слово self)
$a -> ();
echo "<br>";
$b -> ();
echo "<br>";
?>
Получим:
string 'Class A' (length=7) - вызов метода getTest() для объекта $a
string 'Class B' (length=7) - вызов метода getTest() для объекта $b
В классе B убираем метод getTest(), то есть, мы его теперь наследуем от класса A, и никак не переопределяем:
Файл B.php
-- файл B.php --
<?php
namespace ;
class extends
{
const TEST = "Class B";
// Когда мы наследуемся, то практически происходит КОПИПАСТ:
// public function getTest()
// {
// слово self всегда указывает на класс,
// в котором объявленна значение константы TEST
// var_dump(self::TEST);
// }
}
?>
В результате в обоих случаях получим:
- string 'Class A' (length=7) - вызов метода getTest() для объекта $a
- string 'Class A' (length=7) - вызов метода getTest() для объекта $b
Когда интерпритатор, на этапе разбора кода, анализирует классы - проверяет их на отсутствие ошибок, инициализирует какие-то значения - он встречает константу const TEST = "Class A" (в классе A) и встречает метод getTest() и "видит" здесь self::, которая указывает на текущий класс, где эта константа была объявлена (в классе A) и , соответственно, он этой константе присваивает значение "Class A".
Код разобран, значение уже присвоино и в классе B значение константы никак не меняется. И поэтому, в данном случае, мы получили:
- string 'Class A' (length=7) - вызов метода getTest() для объекта $a
- string 'Class A' (length=7) - вызов метода getTest() для объекта $b
Позднее статическое связывание
Чтобы решить эту ситуацию, в PHP было добавленна новая возможность - это позднее статическое связывание, которая возможна после этапа разбора кода.
Позднее статическое связывание - это возможность, после этапа разбора кода, присвоить значение константе (TEST), или какому нибудь статическому свойству ( переопределенному или объявленному в классе - наследнике ) - - не того класса, где она объявленна, а того класса, где она фактически используется (class B).
Для этого, в классе А, мы вместо ключевого слова self, мы можем использовать ключевое слово static.
В классе А определим второй метод getTest2().
Теперь класс В наследует два метода: getTest() и getTest2().
Файл A.php
-- файл A.php --
<?php
namespace ;
class
{
// создадим константу
const TEST = "Class A";
// определяем метод getTest() для того, чтобы получить эту константу вне класса
public function ()
{
// слово self всегда указывает на класс,
// в котором объявленна константа (TEST)
(self::TEST);
//var_dump(A::TEST);
}
// определяем метод getTest2()
public function ()
{
// слово static указывает на тот класс, в котором мы сейчас работаем, то есть,
// если мы работаем в классе В, то оно будет указывать на класс В.
(static::TEST);
}
}
?>
В индексном файле вызовем метод getTes2t() для объекта $b.
Файл index.php:
-- файл index.php --
<?php
use \{ , };
use \ \{ , };
(-1);
require_once . '/vendor/autoload.php';
function ($data)
{
echo '<pre>' . ($data, 1 ) . '</pre>';
}
function ($product)
{
echo "<p>Предлагаем чехол для гаджета {$product -> ()} </p>" ;
}
// Создадим объект класса А и объект класса В
$a = new \ \ ();
$b = new \ \ ();
// Вызовем метод getTest() для объектов $a и $b (где используется ключевое слово self)
$a -> ();
echo "<br>";
$b -> ();
echo "<br>";
// Вызовем метод getTes2t() для объека $b (где используется ключевое слово static)
$b -> ();
echo "<br>";
?>
Получим:
string 'Class A' (length=7)
- вызов метода getTest() для объекта $a (где используется ключевое слово self)
string 'Class B' (length=7)
- вызов метода getTest() для объекта $b (где используется ключевое слово self)
string 'Class B' (length=7)
- вызов метода getTest2() для объекта $b (где используется ключевое слово static)
Цепочка методов
В классе BookProduct объявим два свойства: public $action1 и public $action2 ;
и определим два метода: public function doAction1() и public function doAction1().
В индексном файле выполняем два действия doAction1() и doAction2() над одним и тем же объектом $book и выведем на экран: debug($book) - все будет работать.
Методов, которые мы можем применять к одному и тому же объекту, может быть несколько. Особенно это актуально при работе с базой данных во всяких фреймворках, где мы делаем сложный запрос.
Для облегчения работы применяется запись - цепочка методов. Цепочка методов - это когда к одному объекту применяются по цепочке несколько методов.
Однако, в нашем случае, если мы запишем цепочкой:
- $book -> doAction1() -> doAction2(),
- то это вызовет ошибку, так как ($book -> doAction1()) - не является объектом.
Если мы хотим организовать цепочку методов, то нам неоходимо сделать так, чтобы каждый конкретный метод, возвращал объект, потому что метод мы можем вызвать только для объекта, соответственно такая запись:
- должна вернуть объект.
Объект содержится в псевдопеременной $this, ее мы и возвращаем (return), в методах doAction1() и doAction2():
public function doAction1()
{
// заполняем свойство объекта значением
echo $this -> action1 = '<p>Выполнили дествие 1</p>';
// возвращаем этот самый объект (объект содержится в псевдопеременной $this)
return $this;
}
........
........
........
- тогда $book -> doAction1() - это объект
- и $book -> doAction1() -> doAction2() - тоже объект и т. д.
Можно проверить, что это объекты:
Файл BookProduct.php:
-- файл BookProduct.php --
<?php
namespace ;
use \ \ ;
use \ ;
class extends implements
{
public $numPages;
// объявим два свойства
public $action1;
public $action2;
public function ($name, $price, $numPages)
{
parent :: ($name, $price);
$this -> numPages = $numPages;
$this -> (5);
}
public function ()
{
}
public function ()
{
$out = parent :: ();
$out .= "Цена без скидки: {$this -> price}<br>";
$out .= "Кол-во страниц:: {$this -> cpu}<br>";
$out .= "Скидка: {$this -> ()}%<br>";
return $out;
}
public function ()
{
return $this -> numPages;
}
public function ($name, $price, $numePage=0)
{
// --------------------------
($name);
($price);
($numePage);
}
// объявим два метода
public function ()
{
// заполняем свойство объекта значением
echo $this -> action1 = '<p>Выполнили дествие 1</p>';
// возвращаем это самый объект (объект содержится в псевдопеременной $this)
return $this;
}
public function ()
{
// заполняем свойство объекта значением
echo $this -> action2 = '<p>Выполнили дествие 2</p>';
// возвращаем это самый объект (объект содержится в псевдопеременной $this)
return $this;
}
}
?>
Файл index.php:
-- файл index.php --
<?php
use \{ , };
use \ \{ , };
(-1);
require_once . '/vendor/autoload.php';
function ($data)
{
echo '<pre>' . ($data, 1 ) . '</pre>';
}
function ($product)
{
echo "<p>Предлагаем чехол для гаджета {$product -> ()} </p>";
}
$a = new \ \ ();
$b = new \ \ ();
$a -> ();
echo "<br>";
$b -> ();
echo "<br>";
$b -> ();
echo "<br>";
// Выполняем два действия doAction1() и doAction2() над одним и тем же объектом $book
//$book -> doAction1();
//$book -> doAction2();
//debug($book);
// запись - цепочка методов:
$book -> -> ;
// можно проверить, что это объекты:
//var_dump($book -> doAction1());
//var_dump($book -> doAction1() -> doAction2());
?>
Получим:
string 'Class A' (length=7)
string 'Class A' (length=7)
string 'Class B' (length=7)
Выполнили дествие 1 - вызов метода doAction1()
Выполнили дествие 2 - вызов метода doAction2()
Наверх Наверх