ООП PHP
Автозагрузка и пространства имен
Ключевое слово use
Автоматическая загрузка классов: php.net
Подключение нескольких функций автозагрузки
Замена обратного слэша в пути на прямой
Содержание папки 12 ("Автозагрузка и пространства имен"):
Функция __autoload
__autoload (php.net) - функция автозагрузки (данная функция является устаревшей).
Она вызывается в тот момент, когда PHP встречает необходимость подключения какого нибудь класса и этот класс не подключен. Допустим, мы где-то создаем экземпляр(объект) какого нибудь класса и при этом данный класс не был подключен (с помощью require или include) то, соответственно, PHP ищет функцию автозагрузки __autoload, если ее он находит, он передает ей параметром имя класса, который необходимо подключить. В этой функции __autoload мы можем определить что делать с этим классом (где его искать и как его подключать):
-- индексный файл --
<?php
//...
// В функцию __autoload передан параметр - имя класса($classname)
function ($classname)
{
// путь, где искать этот класс + ".php"
$filename= "./" . $classname . ".php";
// как подключать этот класс
include_once($filename);
}
//...
?>
- эта функция является устаревшей и, в качестве альтернативы, рекомендуется использовать функцию spl_autoload_register.
Функция spl_autoload_register
spl_autoload_register (php.net) - регистрирует заданную функцию в качестве реализации метода __autoload.
Эта функция выполняет тоже самое, что и функция __autoload, но она удобнее.
Функцию __autoload мы можем зарегистрировать только одну, в случае с функцией spl_autoload_register - здесь мы можем описать сколь угодно много функций.
Функция автозагрузки используется для того, чтобы избежать множественных подключений (с помощью require или include и др.).
Опишем функцию автозагрузки:
function autoloder($class)
{
exit($class); // выведем то, что у нас приходит(для проверки)
}
// - в переменную $class будет поступать имя класса.
Нашу функцию автозагрузки необходимо зарегистрировать с помощью функции spl_autoload_register. В качестве первого обязательного параметра в нее передается имя функции, которую необходимо зарегистрировать (которую мы написали):
spl_autoload_register('autoloder');
- когда этот класс не подключен, PHP ищет зарегистрированные функции автозагрузки и вызывает их одну за другой в поисках подключения нужного класса.
Файл index.php
-- index.php --
<?php
(-1);
function ($class)
{
exit($class); // выведем то, что у нас приходит(для проверки)
}
('autoloder');
//...
//...
//...
?>
Выведет:
BookProduct
Все классы лежат у нас в папке classes, напишем путь к этому классу: от текущего каталога (с помощью константы __DIR__), обращамся к папке classes, далее - имя класса ($class) и расширение .php:
- $file = __DIR__ . "/classes/{$class}.php" ; - путь к классу от текущего каталога
- exit($file); - выведет на странице путь к файлу: W:\domains\PHPoop1\12/classes/BookProduct.php
Если теперь распечатать наш файл то, вместо BookProduct мы увидим полный путь к итересующему нас классу (W:\domains\PHPoop1\12/classes/BookProduct.php ).
Остается его подключить. Для этого сначала проверяем, а если там этот класс:
-- индексный файл --
<?php
// Если класс существует (file_exists) по указаному пути ($file), то
// мы его подключим (данная функция вызывается только в том случае,
// если данный класс еще не был подключен, то есть поочередно подключаем все классы )
// проверим, как это работает
if( ($file))
{
require_once $file;
//var_dump($file);
}
?>
Файл index.php
-- файл index.php --
<?php
(-1);
// Закомментируем подключения классов
require_once 'classes/Product.php';
require_once 'classes/I3D.php'; // подключаем интерфейсный файл interface I3D
require_once 'classes/IGadget.php'; // подключаем интерфейс IGadget.php
require_once 'classes/NotebookProduct.php'; // подключаем класс NotebookProduct
require_once 'classes/BookProduct.php'; // подключаем класс BookProduct
// Опишем функцию автозагрузки
function ($class)
{
//exit($class);
// - после обновления страницы получаем - BookProduct - это тот класс, который был не найден
// путь к классу от текущего каталога
$file ="/classes/{$class}.php" ;
//exit($file);
// - получаем путь к файлу W:\domains\PHPoop1\12/classes/BookProduct.php
// подключаем класс BookProduct.php
// Если класс существует (file_exists) по указаному пути ($file), то
// мы его подключим (данная функция вызывается только в том случае,
// если данный класс еще не был подключен,
// то есть поочередно подключаем все классы )
if ($file))
{
require_once $file;
//var_dump($file); // проверим, как это работает
}
}
('autoloder');
// ------------------------------------------------------
function ($data)
{
echo '<pre>' . ($data, 1 ) . '</pre>';
}
function ($product)
{
echo "<p>Предлагаем чехол для гаджета {$product -> ()} </p>";
}
$book = new ('Три мушкетера', 20, 1000);
$notebook = new ('Dell', 1000, 'Intel');
// предлагаем чехол для гаджета Dell
($notebook);
// предлагаем чехол для гаджета Три мушкетёра
($book);
($book);
echo $book -> ();
?>
Получим:
Предлагаем чехол для гаджета Dell
BookProduct Object
(
[numPages] => 1000
[name:Product:private] => Три мушкетера
[price:protected] => 20
[discount:Product:private] => 5
)
О товаре:
Наименование: Три мушкетера
Цена со скидкой: 19
Цена без скидки: 20
Кол-во страниц: 1000
Скидка: 5%
- все работает, при этом никаких подключений классов у нас больше нет.
Подключение нескольких функций автозагрузки
Так как нерационально держать все классы (их может быть очень много) в одной папке classes, то для интерфейсов создадим отдельную папку interfaces, которая будет лежать в папке classes.
Если сейчас обновить страницу - будет ошибка.
Преимущество функции spl_autoload_register заключается в том, что мы можем зарегистрировать несколько функций автозагрузки:
- function autoloder1($class)
- function autoloder2($class)
В функции autoloder2 поменяется логика, здесь мы будем искать не просто в корне папки classes, а в ней есть еще папка interfaces:
- $file = __DIR__ . "/classes/interfaces/{$class}.php".
- и наш интерфейс мы будем искать именно там.
Остается зарегистрировать функцию autoloder2.
У нас получилось две функции автозагрузки и подключаем их одну за другой. PHP их будет вызывать в порядке, как мы их регистрируем: сначала вызовет autoloder1, затем - autoloder2.
Если в функцию поставим необязательные параметры, то третий параметр будет менять очередность загрузки.
Интерфейс I3D.php:
-- интерфейс I3D .php --
<?php
interface
{
// константа интерфейса
const TEST2 = 'test interface';
// метод интерфейса (данный метод не имеет реализацию)
public function ();
}
?>
Интерфейс IGadget:
-- интерфейс IGadget.php --
<?php
// создадим интерфейс IGadget
interface
{
// публичный метод getCase() без реализации
public function ();
}
?>
Файл index.php:
-- index.php --
<?php
(-1);
// первая функция автозагрузки
function ($class)
{
echo 1; // для проверки порядка вызова
// путь к классу от текущего каталога
$file ="/classes/{$class}.php";
// Если класс существует (file_exists) по указаному пути ($file), то
// мы его подключим (данная функция вызывается только в том случае,
// если данный класс еще не был подключен,
// то есть поочередно подключаем все классы )
if ($file))
{
require_once $file;
}
}
// вторая функция автозагрузки
function ($class)
{
echo 2; // для проверки порядка вызова
$file ="/classes/interfaces/{$class}.php";
if ($file))
{
require_once $file;
}
}
// наши функции автозагрузки необходимо зарегестрировать с помощью
// функции spl_autoload_register();
// в качестве первого обязательного параметра передаются имена функций,
// которые мы написали 'autoloder...'
// 3-й параметр меняет очередность загрузки:
('autoloder1');
('autoloder2', true, true);
// ------------------------------------------------------
function ($data)
{
echo '<pre>' . ($data, 1 ) . '</pre>';
}
function ($product)
{
echo "<p>Предлагаем чехол для гаджета {$product -> ()} </p>";
}
$book = new ('Три мушкетера', 20, 1000);
$notebook = new ('Dell', 1000, 'Intel');
// предлагаем чехол для гаджета Dell
($notebook);
// предлагаем чехол для гаджета Три мушкетёра
($book);
($book);
echo $book -> ();
?>
Выведет:
21212212
- 3-й параметр меняет очередность загрузки (сначала будет вызываться функция 'autoloder2')
...
...
...
- после обновления страницы все у нас работает.
Но файлов все равно может быть много, и папок может быть много, кроме того могут быть и сторонние библиотеки, которые тоже лежат по своим папкам. Для больших приложений создавать множество функций автозагрузки - тоже не очень удобно.
В зтом случае нам помогут "пространства имен"
Пространства имен
Пространства имен (php.net) нужны для того, чтобы избежать, в первую очередь, конфликта имен.
Иногда разные классы могут иметь одинаковые имена (например, контроллер может называться news и модель может называться news). Кроме того, у нас может быть свой код, могут быть стороние библиотеки и там может быть совпадение имен классов.
Эту проблему решает пространство имен.
Простаннство имен указывается с помощью ключевого слова namespace, которое записывается в самом начале, перед основным кодом (перед объявлением класса).
В качестве namespace принято указавыть путь к данному классу, начиная от глобального пространства имен, то есть - от корня нашего приложения (в данном случае - это папка 12).
Добавим пространства имен для наших классов Product, NotebookProduct, BookProduct (namespace classes) и итерфейсов I3D, IGadget (namespace classes\interfaces).
Теперь имена классов - составные - включаются пространства имен (например, класс 'BookProduct' - стал 'classes\BookProduct'). Теперь, если мы создадим два класса BookProduct, которые лежат в разных пространствах имен, то все будет работать и мы увидим, что у нас два разных класса.
Чтобы теперь все у нас заработало, необходимо правильно прописать имена классов:
например, не BookProduct, а \classes\BookProduct и т. д.:
- $file = __DIR__ . "/classes/{$class}.php" - в автозагрузчике,
- $book = new \classes\BookProduct ('Три мушкетера', 20, 1000); -
- создание объекта класса classes\BookProduct.
Ключевое слово use
Также это можно сделать с помощью ключевого слова use.
С помощью ключевого слова use мы указываем какие классы мы будем использовать:
- use classes\BookProduct - (в файле index.php).
В автозагрузчике никакой папки дописывать не нужно (в файле index.php).
- $file = __DIR__ . "/{$class}.php" - (вместо $file = __DIR__ . "/classes/interfaces/{$class}.php")
- теперь здесь мы пишем только имя класса, а весь остальной путь берется из namespace
Убираем второй автозагрузчик.
Интерфейс I3D .php
-- интерфейс I3D .php --
<?php
// В качестве namespace - указывается путь к данному классу, начиная от глобального
// пространства имен, то есть от корня нашего приложения (папки 12)
// добавляем пространство имен (обратный слэш !!!)
namespace \ ;
// теперь наш интерфейс лежит в пространстве имен: classes\interfaces
// и называется I3D (classes\interfaces I3D)
interface
{
// константа интерфейса
const TEST2 = 'test interface';
// метод интерфейса (данный метод не имеет реализацию)
public function ();
}
?>
Интерфейс IGadget .php
-- интерфейс IGadget --
<?php
namespace \ ;
// теперь наш интерфейс лежит в пространстве имен: classes\interfaces
// и называется IGadget (classes\interfaces IGadget)
interface
{
// публичный метод getCase() без реализации
public function ();
}
?>
Класс NotebookProduct.php
-- файл NotebookProduct.php --
<?php
// добавляем пространство имен
namespace ;
// импортируем IGadget
use \ \ ;
// теперь наш интерфейс лежит в пространстве имен: classes и
// называется NotebookProduct (classes NotebookProduct)
class extends implements
{
public $cpu;
public function ($name, $price, $cpu)
{
parent :: ($name, $price);
$this -> cpu = $cpu;
}
// в нашем классе мы обязательно должны реализовать интерфейсный метод getCase()
// если интерфейс будет пустым, то ничего реализовывать мы не будем.
public function ()
{
}
public function ()
{
$out = parent :: ();
$out .= "Процессор: {$this -> cpu}<br>";
return $out;
}
public function ()
{
return $this -> cpu;
}
public function ($name, $price, $cpu = '')
{
// --------------------------
($name);
($price);
($cpu);
}
}
?>
Файл index.php
-- файл index.php --
<?php
// с помощью ключевого слова use мы указываем какие классы мы будем использовать
use \ ;
use \ \ ;
use \ \ ;
use \ ;
(-1);
// ПРОСТРАНСТВО ИМЕН начало -----------------------------------------------
// Опишем функцию автозагрузки
function ($class)
{
//var_dump($class); exit;
// теперь здесь мы пишем только имя класса,
// а весь остальной путь берется из namespace
$file =
// ПРОСТРАНСТВО ИМЕН конец ---------------------------------------------------
function ($data)
{
echo '<pre>' . ($data, 1 ) . '</pre>';
}
function ($product)
{
echo "<p>Предлагаем чехол для гаджета {$product -> ()} </p>";
}
$book = new ('Три мушкетера', 20, 1000);
$notebook = new ('Dell', 1000, 'Intel');
// предлагаем чехол для гаджета Dell
($notebook);
// предлагаем чехол для гаджета Три мушкетёра
($book);
($book);
echo $book -> ();
?>
Выведет:
Предлагаем чехол для гаджета Dell
classes\BookProduct Object
(
[numPages] => 1000
[name:classes\Product:private] => Три мушкетера
[price:protected] => 20
[discount:classes\Product:private] => 5
)
О товаре:
Наименование: Три мушкетера
Цена со скидкой: 19
Цена без скидки: 20
Кол-во страниц: 1000
Скидка: 5%
- все работает.
У нас теперь только один автозагрузчик и он успешно работает, потому что нам нужно только имя класса, а оно уже включает в себя и имя класса, и путь, где этот класс лежит.
В этом и заключается преимущество использования namespace:
- во первых, мы избегаем возможного конфликта имен,
- во вторых, нам не нужно массу автозагрузчиков, нам достаточно только одного своего, в котором уже, благодоря namespace, мы будем видеть путь к данному классу.
Групповые объявления use
В PHP7 мы можем группировать одинаковые пространста имен (Групповые объявления use).
Например, если классы ClassA , ClassB , ClassC - лежат в одном пространстве имен classes\namespace\. Соответственно мы можем сгруппировать названия классов в фигурных скобках в одной строке кода. Аналогично можно сгруппировать подключаемые функции и константы.
use
// в нашем случае:
use \{ , };
use \ \{ , };
Замена обратного слэша в пути на прямой
В автозагрузчике выведем наш класс: echo $file = __DIR__ ."/{$class}.php" и завершим работу (exit).
Выведем на экран - увидим, что в пути есть обратные слэши: C:\OSPanel\domains\PHPoop1\12/classes\BookProduct.php, - что недопустимо для ЛИНУКС,
соответственно надо дописать нашу функцию, а именно, нам нужно все обратные слэши заменить прямыми.
С помощью функции str_replace мы ищем обратные слэши (\) и заменяем их на прямые (/) и ищем это в переменной $class, которая передается параметром.
После обновления получим: C:\OSPanel\domains\PHPoop1\12/classes/BookProduct.php
- обратный слэш в classes\BookProduct поменялся на прямой: classes/BookProduct
Файл index.php
-- index.php --
<?php
// с помощью ключевого слова use мы указываем какие классы мы будем использовать
//use classes\BookProduct;
//use classes\interfaces\IGadget;
//use classes\interfaces\I3D;
//use classes\NotebookProduct;
// группируем одинаковые пространства имен
use \{ , };
use \ \{ , };
(-1);
// Опишем функцию автозагрузки
function ($class)
{
$class = ("\\", "/", $class);
// - ищем обратный слэш (здесь (\\) - один экранирующий) и заменяем их
// на прямые, и ищем их в переменной $class, которая передается
// параметром (после запуска увидим, что обратный слэш поменялся на прямой)
$file =
function ($data)
{
echo '<pre>' . ($data, 1 ) . '</pre>';
}
function ($product)
{
echo "<p>Предлагаем чехол для гаджета {$product -> ()} </p>" ;
}
$book = new ('Три мушкетера', 20, 1000);
$notebook = new ('Dell', 1000, 'Intel');
($notebook);
// предлагаем чехол для гаджета Три мушкетёра
($book);
($book);
echo $book -> ();
?>
Выведет:
C:\OSPanel\domains\PHPoop1\12/classes/BookProduct.php
Наверх Наверх