Правильное подключение классов в PHP
Всем привет, учу ООП в PHP и столкнулся с проблемой правильного подключение классов. У меня есть форма регистрации в html файле, данные с которой обрабатываются php скриптами. У меня есть один родительский класс и несколько дочерних, все они в разных файлах. Я хочу автоматически подгружать все дочерние классы в родительский. Вот мой родительский класс в котором я инициализирую объект дочернего.
); $addTxt = new AddTxt(); /*инициализарую объект дочернего класса*/ class Validator < public $name = 'name '; public $s_name = 's_name '; public $email = 'email '; public $ticket = 'ticket '; function __construct()< $this->name = $_POST['name']; $this->s_name = $_POST['s_name']; $this->email = $_POST['email']; $this->ticket = $_POST['ticket']; > > $validate = new Validator();
А вот, собственно, и дочерний класс
date = date('d_m_Y'); $this->file = 'registration_' . $this->date . ".txt"; $this->string = $this->name . " " . $this->s_name . " " . $this->email . " " . $this->ticket . PHP_EOL; > function addLine()< if(file_exists($this->file))< $f = fopen($this->file, "a+") or die ("Error"); if (($f) && filesize($this->file)) < $lines = explode("\n", fread($f, filesize($this->file))); foreach($lines as $line) < $l = explode(" ", $line); $line_items[] = $l[2]; >foreach ($line_items as $item) < if($item === $this->email) < die ("Такой email уже существует"); >> fwrite($f, $this->string); > else < fwrite($f, $this->string); > fclose($f); > if(!file_exists($this->file)) < $f = fopen($this->file, "a+") or die ("Error"); fwrite($f, $this->string); fclose($f); > > > $addTxt = new AddTxt();/*повторная инициализация объекта дочернего класса*/ $addTxt->addLine()
Как видно, объект дочернего класс инициализирован дважды, иначе я получаю вот такую ошибку Call to a member function addLine() on a non-object , то есть, будто я не создал объект. Мне кажется, что это неверный способ. Я хочу инициализировать объект только один раз. Подскажите как правильно подключение классов?
Объектно-ориентированный PHP: автоматическая загрузка классов, сериализация и получение информации об объектах
Добро пожаловать в четвертый урок из серии, посвященной ООП в PHP! Если вы пропустили первые три урока, вам возможно, захочется рассмотреть и их, дабы узнать подробнее о классах и объектах в PHP:
- Объектно-ориентированный PHP: специально для начинающих
- Объектно-ориентированный PHP: подробнее о методах и полях класса
- Объектно-ориентированный PHP: работа с наследованием
Если вы прошли все три предыдущих урока, то вы уже знакомы с основными концепциями ООП в PHP: классами, объектами, методами, полями и наследованием. В этом, последнем, уроке я постараюсь завершить тему объектно-ориентированного программирования в PHP, а также познакомить вас с некоторыми чрезвычайно полезными свойствами PHP, связанных с ООП:
- Как автоматически загружать классы;
- Как конвертировать объекты в строку и строку в объекты для удобства хранения и передачи;
- Как получить подробную информацию об определенном классе или объекте.
Автоматическая загрузка классов
Как правило, полезно хранить каждый PHP класс в отдельном файле. Например, в приложении веб-форума, вы бы хранили класс Member в файле Member.php, а класс Topic — в файле Topic.php. Скорее всего, вы будете хранить все эти файлы в папке classes где-то на сайте:
classes/ Member.php Topic.php
Затем, если понадобится создать объект класса, например, Member, PHP сначала придется подключить файл с этим классом:
Хранение классов таким образом — вещь полезная не только для вашей психики, но и для использования такого удобного свойства, как автоматическая загрузка (autoloading).
Автоматическая загрузка работает так. Где-то в начале приложения PHP вы создаете специальную функцию __autoload(). В последствии, если где-то в коде будет попытка создать объект класса, о котором ничего не известно, PHP автоматически вызовет данную функцию, передав ей в качестве параметра имя искомого класса. Вся работа функции заключается в том, чтобы найти нужный файл и подгрузить его к скрипту, тем самым загрузить сам класс. После этого PHP уже сможет создать объект данного класса.
Давайте приведем пример. Напишем функцию __autoload() для автоматической загрузки классов из папки classes:
"; > $member = new Member(); echo "Created object: "; print_r( $member ); ?>
Вот, как это работает. Сперва, создаем функцию __autoload() с входным параметром $className. В начале, функция убирает все подстроки “..” из полученного параметра, это делается в целях безопасности. Затем, с помощью функции require_once(), она подгружает нужный файл. Функции известно, что он находится в папке classes, и его расширение — .php. Функция также выводит на страницу сообщение, так что мы сразу увидим, что она отработала.
Затем протестим нашу функцию, создав объект класса Member. Так как мы предварительно не подгрузили файл с данным классом, PHP запустит функцию __autoload(), передав ей имя класса — “Member”. Она, в свою очередь, будет искать файл classes/Member.php. Затем PHP создает-таки объект Member. В завершении, отображаем сообщение о том, что объект создан.
Чтобы протестировать работу данного скрипта, создадим папку classes в том же каталоге, что и скрипт. Создадим в ней файл Member.php с простеньким классом:
Теперь, когда мы запустим скрипт, загрузится файл classes/Member.php, создастся объект класса Member, и на экране отобразится следующее:
Loaded classes/Member.php Created object: Member Object ( )
Автоматическая загрузка поможет вам сэкономить уйму времени, тем более, если у вас огромное количество классов. Вместо того, чтобы вызывать функцию require_once() в начале каждого файла, вы просто создаете в начале всего приложения функцию __autoload(), и отдаете управление загрузкой классов в руки PHP!
Сериализация объектов
Следующее свойство ООП, о котором я расскажу, — это конвертация объекта в строку и, наоборот, строки в объект. Это может быть полезно в случаях, когда вам нужно передавать объекты между скриптами и даже приложениями. Вот конкретные случаи:
- Передача объектов через поля веб-форм;
- Передача объектов через адресную строку;
- Хранение объекта в текстовом файле или одном поле таблицы базы данных.
Для конвертации объекта в строку, и обратно, используются следующие функции:
- serialize() — принимает объект и возвращает строковое представление его класса и свойств;
- unserialize() — принимает строку, созданную при помощи serialize(), и возвращает объект.
Давайте посмотрим на эти две функции в деле:
loggedIn = true; > public function logout() < $this->loggedIn = false; > public function isLoggedIn() < return $this->loggedIn; > > $member = new Member(); $member->username = "Fred"; $member->login(); $memberString = serialize( $member ); echo "Converted the Member object to a string: '$memberString'
"; echo "Converting the string back to an object.
"; $member2 = unserialize( $memberString ); echo $member2->username . " is " . ( $member2->isLoggedIn() ? "logged in" : "logged out" ) . "
"; ?>
Мы создали простенький класс Member с полем public $username, полем private $loggedIn и тремя методами public: login(), logout() и isLoggedIn(). Затем наш скрипт создает объект класса Member, дает ему имя «Fred» и логинит его.
Затем вызываем функцию serialize(), передав ей объект класса Member. serialize() возвращает строковое представление данного объекта, которое мы сохраним в переменной $memberString и отобразим на странице:
Converted the Member object to a string: 'O:6:"Member":2:'
Затем конвертируем нашу строку обратно в объект класса Member, вызвав функцию unserialize(), и сохраняем полученный объект в переменной $member2. Чтобы проверить, что наш объект сконвертировался верно и полностью, мы отображаем значение его поля $username и вызываем его метод isLoggedIn(), чтобы проверить, залогинился ли пользователь. Вот, что отобразится на странице:
Converting the string back to an object. Fred is logged in
Как видите, строка, созданная функцией serialize(), содержит имя класса, а также названия всех его полей и их значения для конкретного объекта. (Перед полями private записывается имя класса, без пробелов.) Тем не менее, названия методов класса не записываются в строку.
Чтобы сработала функция unserialize(), класс объекта, который должен быть сконвертирован из строки, должен подгружаться еще до того, как идет вызов unserialize(). Вы можете написать сам класс в том же скрипте, где вызывается unserialize(), или подгрузить файл с данным классом через функцию require_once(). Также вы можете создать функцию __autoload(), о которой мы говорили ранее. PHP вызовет __autoload(), если не сможет найти класс, объект которого вы пытаетесь сконвертировать.
На заметку: функции serialize() и unserialize() работают также и с другими типами данных, таких как массивы. Тем не менее, они не работают с ресурсами.
Функции __sleep() и __wakeup()
Иногда, перед сериализацией объекта, возникает необходимость кое-что почистить. Например, может понадобиться записать объект в базу данных и закрыть соединение. Подобно этому, после десериализации, вы захотите восстановить соединение и произвести еще какие-то действия.
В PHP есть несколько специальных методов, которые помогут вам в этом:
- __sleep() вызывается строго перед тем, как объект сериализуется с помощью функции serialize().
- __wakeup() вызывается сразу после того, как объект десериализуется с помощью unserialize().
Ваша функция __sleep() должна будет возвращать список полей класса. Именно тех полей, которые функция serialize() включит в возвращаемую строку. Вы можете использовать это для того, чтобы исключить ненужные поля из строкового представления объекта.
Давайте добавим в наш предыдущий пример методы __sleep() и __wakeup():
loggedIn = true; > public function logout() < $this->loggedIn = false; > public function isLoggedIn() < return $this->loggedIn; > public function __sleep() < echo "Cleaning up the object.
"; return array( "username" ); > public function __wakeup() < echo "Setting up the object.
"; > > $member = new Member(); $member->username = "Fred"; $member->login(); $memberString = serialize( $member ); echo "Converted the Member object to a string: '$memberString'
"; echo "Converting the string back to an object.
"; $member2 = unserialize( $memberString ); echo $member2->username . " is " . ( $member2->isLoggedIn() ? "logged in" : "logged out" ) . "
"; ?>
Вот, что отобразится на странице:
Cleaning up the object. Converted the Member object to a string: 'O:6:"Member":1:' Converting the string back to an object. Setting up the object. Fred is logged out
Обратите внимание на то, что:
- Наши методы __sleep() и __wakeup() в действительности ничего не подчищают и не задают; вместо этого, они просто выводят сообщения «Cleaning up the object. » и «Setting up the object. «.
- Так как мы включаем только поле $username в массив, который возвращает __sleep(), в результирующей строке не будет присутствовать поле $loggedIn.
- В результате, поле $loggedIn десериализованного объекта примет значение по умолчанию — false, поэтому при вызове метода isLoggedIn() от десериализованного объекта, он вернет false. Вот, почему скрипт отобразит сообщение «Fred is logged out».
Если вы хотите написать метод __sleep() и чтобы все поля были сериализованы, тогда вам понадобится перечислять все поля для массива, который возвращает метод __sleep(). Этого легко достичь при помощи функций PHP array_keys() и get_object_vars() таким образом:
public function __sleep() < // почистить return array_keys( get_object_vars( $this ) ); >
На заметку: другой классный способ сериализации объектов (причем, кросс-платформенный) — это конвертирование строк JSON.
Получение информации о классах и объектах
В PHP есть много полезных функций для получения информации о классах и объектах. Это полезно в случаях, когда нужно работать с объектами из другого приложения, а также поможет вам написать удобный, гибкий код. Используя эти функции для объекта, вы можете получить имя его класса, проверить, создан ли он от определенного класса, получить его поля и методы и др.
Вот краткий перечень наиболее часто употребляемых из них:
Функция | Описание |
get_class( $obj ) | Возвращает имя класса, которому принадлежит объект $obj |
get_parent_class($className или $obj ) | Возвращает имя класса, который является родителем класса $className, или родителем класса, чьим объектом является $obj |
is_a( $obj, $className ) | Возвращает true, если $obj — объект класса $className, или если $obj — объект класса — потомка $className |
$obj instanceof $className | Делает то же самое, что и функция is_a() |
get_class_methods($className или $obj ) | Возвращает массив названий всех методов класса $className или объекта $obj |
get_class_vars( $className ) | Возвращает ассоциативный массив всех полей класса $className вместе с их значениями по умолчанию |
get_object_vars( $obj ) | Возвращает ассоциативный массив всех полей объекта $obj, вместе с их текущими значениями |
Важно отметить, что такие функции, как get_class_methods(), get_class_vars() и get_object_vars(), возвращают только те методы и поля, которые находятся в одной области действия с кодом, их вызывающим. Например, метод private будет включен в возвращаемое значение функции get_class_methods(), только если get_class_methods() вызывается внутри метода класса.
Давайте поэкспериментируем с некоторыми из этих функций на таком примере:
loggedIn = true; > public function logout() < $this->loggedIn = false; > public function isLoggedIn() < return $this->loggedIn; > public function listMyProperties() < echo "My properties are: "; print_r( get_object_vars( $this ) ); >> class Administrator extends Member < public $adminLevel = 1; public function createForum( $forumName ) < echo "$this->username created a new forum: $forumName
"; > public function banMember( $member ) < echo "$this->username banned the member: $member->username
"; > > $admin = new Administrator(); $admin->username = "Mary"; $admin->login(); echo "1. $admin->username's class is " . get_class( $admin ) . "
"; echo "2. $admin->username's parent class is " . get_parent_class( $admin ) . "
"; echo "3. Is $admin->username a Member? " . ( is_a( $admin, "Member" ) ? "Yes" : "No" ) . "
"; echo "4. $admin->username's methods are: "; print_r( get_class_methods( $admin ) ); echo "
5. $admin->username's class properties are: "; print_r( get_class_vars( get_class( $admin ) ) ); echo "
6. $admin->username's object properties are: "; print_r( get_object_vars( $admin ) ); echo "
7. "; $admin->listMyProperties(); ?>
В скрипте создается класс Member с несколькими полями и методами. Также в нем создается класс Administrator — дочерний от класса Member. В классе Administrator добавляется поле public $adminLevel, а также пара методов: createForum() и banMember(), которые будут только у объектов класса Administrator.
После создания класса, мы создаем объект класса Administrator, сохраняем его в переменной $admin, даем ему имя «Mary» и логиним админа при помощи вызова метода login().
В последней части скрипта (строки 44-54), мы применяем различные функции к нашим объектам и классам. Вот результат работы данного кода:
Mary's class is Administrator Mary's parent class is Member Is Mary a Member? Yes Mary's methods are: Array ( [0] => createForum [1] => banMember [2] => login [3] => logout [4] => isLoggedIn [5] => listMyProperties ) Mary's class properties are: Array ( [adminLevel] => 1 [username] => ) Mary's object properties are: Array ( [adminLevel] => 1 [username] => Mary ) My properties are: Array ( [adminLevel] => 1 [username] => Mary [loggedIn] => 1 )
Вот, как отработают данные функции:
- get_class( $admin ) возвращает строковое значение «Administrator».
- get_parent_class( $admin ) возвращает строковое значение «Member».
- is_a( $admin, «Member» ) возвращает true.
- get_class_methods( $admin ) возвращает массив названий методов для классов Administrator и Member. Так как все эти методы — public, они все будут записаны в массив.
- get_class_vars( $admin ) возвращает массив названий полей public и соответствующих им значений по умолчанию для классов Administrator и Member.
- get_object_vars( $admin ) возвращает массив названий полей public и соответствующих им текущих значений.
- В конце вызываем метод класса Member — listMyProperties(). Этот метод вызывает функцию get_object_vars() и отображает результат. Тем не менее, так как get_object_vars() теперь вызывается изнутри класса, он также возвращает поле private — $loggedIn.
Как видите, в PHP очень легко извлечь любую информацию об объекте или классе. В PHP есть еще функции для получения инфы об объекте, классе и интерфейсе, например, class_exists(), get_called_class(), get_declared_classes(), get_declared_interfaces(), interface_exists(), is_subclass_of(), method_exists() и property_exists().
Итоги
В этом уроке вы ознакомились с тремя полезнейшими свойствами PHP объектов:
- Использование функции __autoload() для автоматической загрузки файлов с необходимыми классами;
- Как конвертировать объекты в строки и обратно при помощи функций PHP serialize() и unserialize();
- Получение информации об объектах, классах, предках при помощи PHP функций.
С помощью этих трех свойств работать с классами и объектами в PHP не составит огромного труда.
Надеюсь, вам понравилась данная серия уроков, посвященная ООП в PHP! Если вы прошли все 4 урока, у вас достаточно знаний для написания классных PHP сайтов и приложений. Однако, на этом история не заканчивается — в PHP еще много полезных свойств, предназначенных для работы с ООП, например:
- Итерация: как последовательно пройтись по всем полям объекта;
- Больше специальных методов, включая __callStatic(), __isset(), __unset(), __toString(), __invoke(), __set_state() и __clone()
- Паттерны: техника, позволяющая достичь много целей при помощи объектов;
- Копирование и сравнение объектов;
- Статическое позднее связывание, которое дает кучу возможностей при вызове статических наследуемых методов.
Если у меня будет время, и если у вас будет такая необходимость, я обязательно расскажу о них в следующих уроках.
5 последних уроков рубрики «PHP»
Фильтрация данных с помощью zend-filter
Когда речь идёт о безопасности веб-сайта, то фраза «фильтруйте всё, экранируйте всё» всегда будет актуальна. Сегодня поговорим о фильтрации данных.
Контекстное экранирование с помощью zend-escaper
Обеспечение безопасности веб-сайта — это не только защита от SQL инъекций, но и протекция от межсайтового скриптинга (XSS), межсайтовой подделки запросов (CSRF) и от других видов атак. В частности, вам нужно очень осторожно подходить к формированию HTML, CSS и JavaScript кода.
Подключение Zend модулей к Expressive
Expressive 2 поддерживает возможность подключения других ZF компонент по специальной схеме. Не всем нравится данное решение. В этой статье мы расскажем как улучшили процесс подключение нескольких модулей.
Совет: отправка информации в Google Analytics через API
Предположим, что вам необходимо отправить какую-то информацию в Google Analytics из серверного скрипта. Как это сделать. Ответ в этой заметке.
Подборка PHP песочниц
Подборка из нескольких видов PHP песочниц. На некоторых вы в режиме online сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.
Способы подключения класса.
Решил реализовать некоторый web интерфейс к СУБД mySql (скажем список приобретенных товаров). Мой класс — собственно занимается контролем вводимой информации, и формированием нормально форматированного вывода, обработкой ошибок, формированием запросов к БД. Но что бы иметь возможность при формировании SQL запросов использовать такие кошерные вещи как плейсхолдеры и отлов ошибок, скачал библиотеку goDB. Возник вопрос — как правильно подключить ее к своему классу, что бы можно было бы пользоваться ее методами?
Мой класс (примерно). Тут может много ошибок, так как я только начал осваивать ООП
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
class Device{ var $device= array('id','name','type','buyDate','sealPeriod','sealExpDate','owner','outOfOrder','OutOfSeal',invNumber,serialNumber); //$name; -Наименование устройства //$type; -Тип устройства //$buyDate; -Дата покупки //$sealPeriod; -Гарвнтийный срок //$sealExpDate; -Срок оканчания гарантии (ваще не знаю нужно ли это свойство т.к. можно вычислить) //$owner; -Владелец (вот тут надо подумать с форматом, так как некоторые хз где) //$outOfOrder; -Списан (boolean) //$outOfSeal; -Истек гарантийный срок //$invNumber -Инвентарный номер //$serialNumber -Серийный номер function getDevices($device){ $result = $db("SELECT * FROM 'devices' WHERE 'name'=:name?",$device); $this->Device = $result; foreach ($this as $key){ echo $key; } } function setDevice($device){ $this->Device = $device; } }
Класс goDB (конструктор и ничего лишнего)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
class goDB extends mysqli implements goDBI { /*** PUBLIC: ***/ /** * Конструктор. * * Отличия от конструктора mysqli (http://php.net/manual/en/mysqli.connect.php): * 1. Исключение при ошибке подключения * 2. Дополнительный формат вызова: один аргумент - массив параметров * 3. Установка кодировки * * @link http://pyha.ru/go/godb/connect/ * * @throws goDBExceptionConnect * не подключиться или не выбрать базу * * @param mixed $host [optional] * хост для подключения (возможен вариант "host:port") * либо массив всех параметров * @param string $username [optional] * имя пользователя mysql * @param string $passwd [optional] * пароль пользователя mysql * @param string $dbname [optional] * имя базы данных * @param int $port [optional] * порт для подключения * в случае указания аргументом и в строке $host используется аргумент * @param string $socket [optional] * mysql-сокет для подключения */ public function __construct($host = null, $username = null, $passwd = null, $dbname = null, $port = null, $socket = null) { if (is_array($host)) { $config = $host; $fields = array( 'host', 'username', 'passwd', 'dbname', 'port', 'socket', 'charset', 'debug', 'prefix', ); foreach ($fields as $field) { $$field = isset($config[$field]) ? $config[$field] : null; } $this->setPrefix($prefix); $this->setDebug($debug); } if (!$port) { $host = explode(':', $host, 2); $port = empty($host[1]) ? null : $host[1]; $host = $host[0]; } @parent::__construct($host, $username, $passwd, $dbname, $port, $socket); if (mysqli_connect_errno()) { throw new goDBExceptionConnect(mysqli_connect_error(), mysqli_connect_errno()); } if (!empty($charset)) { $this->set_charset($charset); } }
1 2 3 4 5 6 7 8 9 10 11 12 13
require('/../lib/godb/godb.php'); $config = array( 'host' => 'localhost', 'username' => 'hunty', 'password' => '123', 'dbname' => 'devisedb', 'charset' => 'utf-8', 'debug' => true ); $db = new goDB($config);
Как подключить класс php
Каждое определение класса начинается с ключевого слова class , затем следует имя класса, и далее пара фигурных скобок, которые заключают в себе определение свойств и методов этого класса.
Именем класса может быть любое слово, при условии, что оно не входит в список зарезервированных слов PHP, начинается с буквы или символа подчёркивания и за которым следует любое количество букв, цифр или символов подчёркивания. Если задать эти правила в виде регулярного выражения, то получится следующее выражение: ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$ .
Класс может содержать собственные константы, переменные (называемые свойствами) и функции (называемые методами).
Пример #1 Простое определение класса
class SimpleClass
// объявление свойства
public $var = ‘значение по умолчанию’ ;
?php
// объявление метода
public function displayVar () echo $this -> var ;
>
>
?>
Псевдопеременная $this доступна в том случае, если метод был вызван в контексте объекта. $this — значение вызывающего объекта.
Внимание
Вызов нестатического метода статически вызывает ошибку Error . До PHP 8.0.0 это привело бы к уведомлению об устаревании, и $this не была бы определена.
Пример #2 Некоторые примеры псевдо-переменной $this
class A
function foo ()
if (isset( $this )) echo ‘$this определена (‘ ;
echo get_class ( $this );
echo «)\n» ;
> else echo «\$this не определена.\n» ;
>
>
>
?php
$a = new A ();
$a -> foo ();
$b = new B ();
$b -> bar ();
Результат выполнения данного примера в PHP 7:
$this определена (A) Deprecated: Non-static method A::foo() should not be called statically in %s on line 27 $this не определена. Deprecated: Non-static method A::foo() should not be called statically in %s on line 20 $this не определена. Deprecated: Non-static method B::bar() should not be called statically in %s on line 32 Deprecated: Non-static method A::foo() should not be called statically in %s on line 20 $this не определена.
Результат выполнения данного примера в PHP 8:
$this определена (A) Fatal error: Uncaught Error: Non-static method A::foo() cannot be called statically in %s :27 Stack trace: #0 thrown in %s on line 27
Классы, доступные только для чтения
Начиная с PHP 8.2.0, класс может быть помечен модификатором readonly . Пометка класса как readonly добавит модификатор readonly к каждому объявленному свойству и предотвратит создание динамических свойств. Более того, невозможно добавить их поддержку с помощью атрибута AllowDynamicProperties . Попытка это сделать приведёт к ошибке компиляции.
#[ \AllowDynamicProperties ]
readonly class Foo >
?php
// Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo
?>
Поскольку ни нетипизированные, ни статические свойства не могут быть помечены модификатором readonly , классы, доступные только для чтения также не могут их объявлять:
// Fatal error: Readonly property Foo::$bar must have type
?>
readonly class Foo
public static int $bar ;
>
?php
// Fatal error: Readonly class Foo cannot declare static properties
?>
Класс readonly может быть расширен тогда и только тогда, когда дочерний класс также является классом readonly .
new
Для создания экземпляра класса используется директива new . Новый объект всегда будет создан, за исключением случаев, когда он содержит конструктор, в котором определён вызов исключения в случае возникновения ошибки. Рекомендуется определять классы до создания их экземпляров (в некоторых случаях это обязательно).
Если с директивой new используется строка ( string ), содержащая имя класса, то будет создан новый экземпляр этого класса. Если имя находится в пространстве имён, то оно должно быть задано полностью.
Замечание:
В случае отсутствия аргументов в конструктор класса, круглые скобки после названия класса можно опустить.
Пример #3 Создание экземпляра класса
// Это же можно сделать с помощью переменной:
$className = ‘SimpleClass’ ;
$instance = new $className (); // new SimpleClass()
?>
Начиная с PHP 8.0.0, поддерживается использование оператора new с произвольными выражениями. Это позволяет создавать более сложные экземпляры, если выражение представлено в виде строки ( string ). Выражения должны быть заключены в круглые скобки.
Пример #4 Создание экземпляра с использованием произвольного выражения
В данном примере мы показываем несколько вариантов допустимых произвольных выражений, которые представляют имя класса. Пример вызова функции, конкатенации строк и константы ::class .
class ClassA extends \stdClass <>
class ClassB extends \stdClass <>
class ClassC extends ClassB <>
class ClassD extends ClassA <>
function getSomeClass (): string
return ‘ClassA’ ;
>
var_dump (new ( getSomeClass ()));
var_dump (new ( ‘Class’ . ‘B’ ));
var_dump (new ( ‘Class’ . ‘C’ ));
var_dump (new ( ClassD ::class));
?>
Результат выполнения данного примера в PHP 8:
object(ClassA)#1 (0) < >object(ClassB)#1 (0) < >object(ClassC)#1 (0) < >object(ClassD)#1 (0)
В контексте класса можно создать новый объект через new self и new parent .
Когда происходит присвоение уже существующего экземпляра класса новой переменной, то эта переменная будет указывать на этот же экземпляр класса. То же самое происходит и при передаче экземпляра класса в функцию. Копию уже созданного объекта можно создать через её клонирование.
Пример #5 Присваивание объекта
$instance = new SimpleClass ();
$assigned = $instance ;
$reference =& $instance ;
$instance -> var = ‘$assigned будет иметь это значение’ ;
$instance = null ; // $instance и $reference становятся null
var_dump ( $instance );
var_dump ( $reference );
var_dump ( $assigned );
?>
Результат выполнения данного примера:
NULL NULL object(SimpleClass)#1 (1) < ["var"]=>string(30) "$assigned будет иметь это значение" >
Создавать экземпляры объекта можно двумя способами:
Пример #6 Создание новых объектов
class Test
static public function getNew ()
return new static;
>
>
?php
class Child extends Test
<>
$obj1 = new Test ();
$obj2 = new $obj1 ;
var_dump ( $obj1 !== $obj2 );
$obj3 = Test :: getNew ();
var_dump ( $obj3 instanceof Test );
$obj4 = Child :: getNew ();
var_dump ( $obj4 instanceof Child );
?>
Результат выполнения данного примера:
bool(true) bool(true) bool(true)
Обратиться к свойству или методу только что созданного объекта можно с помощью одного выражения:
Пример #7 Доступ к свойствам/методам только что созданного объекта
echo (new DateTime ())-> format ( ‘Y’ );
?>?php
Результатом выполнения данного примера будет что-то подобное:
2016
Замечание: До PHP 7.1 аргументы не имели значения, если не определена функция конструктора.
Свойства и методы
Свойства и методы класса живут в разделённых «пространствах имён», так что возможно иметь свойство и метод с одним и тем же именем. Ссылки как на свойства, так и на методы имеют одинаковую нотацию, и получается, что получите вы доступ к свойству или же вызовете метод — определяется контекстом использования.
Пример #8 Доступ к свойству vs. вызов метода
public function bar () return ‘метод’ ;
>
>
$obj = new Foo ();
echo $obj -> bar , PHP_EOL , $obj -> bar (), PHP_EOL ;
Результат выполнения данного примера:
свойство метод
Это означает, что вызвать анонимную функцию, присвоенную переменной, напрямую не получится. Вместо этого свойство должно быть назначено, например, переменной. Можно вызвать такое свойство напрямую, заключив его в скобки.
Пример #9 Вызов анонимной функции, содержащейся в свойстве
public function __construct () $this -> bar = function() return 42 ;
>;
>
>
echo ( $obj -> bar )(), PHP_EOL ;
Результат выполнения данного примера:
extends
Класс может наследовать константы, методы и свойства другого класса используя ключевое слово extends в его объявлении. Невозможно наследовать несколько классов, один класс может наследовать только один базовый класс.
Наследуемые константы, методы и свойства могут быть переопределены (за исключением случаев, когда метод или константа класса объявлены как final) путём объявления их с теми же именами, как и в родительском классе. Существует возможность доступа к переопределённым методам или статическим свойствам путём обращения к ним через parent::
Замечание: Начиная с PHP 8.1.0, константы можно объявлять окончательными (final).
Пример #10 Простое наследование классов
class ExtendClass extends SimpleClass
// Переопределение метода родителя
function displayVar ()
echo «Расширенный класс\n» ;
parent :: displayVar ();
>
>
?php
$extended = new ExtendClass ();
$extended -> displayVar ();
?>
Результат выполнения данного примера:
Расширенный класс значение по умолчанию
Правила совместимости сигнатуры
При переопределении метода его сигнатура должна быть совместима с родительским методом. В противном случае выдаётся фатальная ошибка или, до PHP 8.0.0, генерируется ошибка уровня E_WARNING . Сигнатура является совместимой, если она соответствует правилам контравариантности, делает обязательный параметр необязательным, добавляет только необязательные новые параметры и не ограничивает, а только ослабляет видимость. Это известно как принцип подстановки Барбары Лисков или сокращённо LSP. Правила совместимости не распространяются на конструктор и сигнатуру private методов, они не будут выдавать фатальную ошибку в случае несоответствия сигнатуры.
Пример #11 Совместимость дочерних методов
class Base
public function foo ( int $a ) echo «Допустимо\n» ;
>
>
class Extend1 extends Base
function foo ( int $a = 5 )
parent :: foo ( $a );
>
>
class Extend2 extends Base
function foo ( int $a , $b = 5 )
parent :: foo ( $a );
>
>
$extended1 = new Extend1 ();
$extended1 -> foo ();
$extended2 = new Extend2 ();
$extended2 -> foo ( 1 );
Результат выполнения данного примера:
Допустимо Допустимо
Следующие примеры демонстрируют, что дочерний метод, который удаляет параметр или делает необязательный параметр обязательным, несовместим с родительским методом.
Пример #12 Фатальная ошибка, когда дочерний метод удаляет параметр
class Base
public function foo ( int $a = 5 ) echo «Допустимо\n» ;
>
>
class Extend extends Base
function foo ()
parent :: foo ( 1 );
>
>
Результат выполнения данного примера в PHP 8 аналогичен:
Fatal error: Declaration of Extend::foo() must be compatible with Base::foo(int $a = 5) in /in/evtlq on line 13
Пример #13 Фатальная ошибка, когда дочерний метод делает необязательный параметр обязательным.
class Base
public function foo ( int $a = 5 ) echo «Допустимо\n» ;
>
>
class Extend extends Base
function foo ( int $a )
parent :: foo ( $a );
>
>
Результат выполнения данного примера в PHP 8 аналогичен:
Fatal error: Declaration of Extend::foo(int $a) must be compatible with Base::foo(int $a = 5) in /in/qJXVC on line 13
Внимание
Переименование параметра метода в дочернем классе не является несовместимостью сигнатуры. Однако это не рекомендуется, так как приведёт к Error во время выполнения, если используются именованные аргументы.
Пример #14 Ошибка при использовании именованных аргументов и параметров, переименованных в дочернем классе
class A public function test ( $foo , $bar ) <>
>
class B extends A public function test ( $a , $b ) <>
>
// Передача параметров согласно контракту A::test()
$obj -> test ( foo : «foo» , bar : «bar» ); // ОШИБКА!
Результатом выполнения данного примера будет что-то подобное:
Fatal error: Uncaught Error: Unknown named parameter $foo in /in/XaaeN:14 Stack trace: #0 thrown in /in/XaaeN on line 14
::class
Ключевое слово class используется для разрешения имени класса. Чтобы получить полное имя класса ClassName , используйте ClassName::class . Обычно это довольно полезно при работе с классами, использующими пространства имён.
Пример #15 Разрешение имени класса
Результат выполнения данного примера:
NS\ClassName
Замечание:
Разрешение имён класса с использованием ::class происходит на этапе компиляции. Это означает, что на момент создания строки с именем класса автозагрузки класса не происходит. Как следствие, имена классов раскрываются, даже если класс не существует. Ошибка в этом случае не выдаётся.
Пример #16 Отсутствует разрешение имени класса
print Does\Not\Exist ::class;
?>?php
Результат выполнения данного примера:
Does\Not\Exist
Начиная с PHP 8.0.0, константа ::class также может использоваться для объектов. Это разрешение происходит во время выполнения, а не во время компиляции. То же самое, что и при вызове get_class() для объекта.
Пример #17 Разрешение имени объекта
namespace NS class ClassName >
>
$c = new ClassName ();
print $c ::class;
?>?php
Результат выполнения данного примера:
NS\ClassName
Методы и свойства Nullsafe
Начиная с PHP 8.0.0, к свойствам и методам можно также обращаться с помощью оператора «nullsafe»: ?-> . Оператор nullsafe работает так же, как доступ к свойству или методу, как указано выше, за исключением того, что если разыменование объекта выдаёт null , то будет возвращён null , а не выброшено исключение. Если разыменование является частью цепочки, остальная часть цепочки пропускается.
Аналогично заключению каждого обращения в is_null() , но более компактный.
Пример #18 Оператор Nullsafe
// Начиная с PHP 8.0.0, эта строка:
$result = $repository ?-> getUser ( 5 )?-> name ;
// Эквивалентна следующему блоку кода:
if ( is_null ( $repository )) $result = null ;
> else $user = $repository -> getUser ( 5 );
if ( is_null ( $user )) $result = null ;
> else $result = $user -> name ;
>
>
?>
Замечание:
Оператор nullsafe лучше всего использовать, когда null считается допустимым и ожидаемым значением для возвращаемого свойства или метода. Для индикации ошибки предпочтительнее выбрасывать исключение.
User Contributed Notes 11 notes
15 years ago
I was confused at first about object assignment, because it’s not quite the same as normal assignment or assignment by reference. But I think I’ve figured out what’s going on.
First, think of variables in PHP as data slots. Each one is a name that points to a data slot that can hold a value that is one of the basic data types: a number, a string, a boolean, etc. When you create a reference, you are making a second name that points at the same data slot. When you assign one variable to another, you are copying the contents of one data slot to another data slot.
Now, the trick is that object instances are not like the basic data types. They cannot be held in the data slots directly. Instead, an object’s «handle» goes in the data slot. This is an identifier that points at one particular instance of an obect. So, the object handle, although not directly visible to the programmer, is one of the basic datatypes.
What makes this tricky is that when you take a variable which holds an object handle, and you assign it to another variable, that other variable gets a copy of the same object handle. This means that both variables can change the state of the same object instance. But they are not references, so if one of the variables is assigned a new value, it does not affect the other variable.
// Assignment of an object
Class Object public $foo = «bar» ;
>;
$objectVar = new Object ();
$reference =& $objectVar ;
$assignment = $objectVar
//
// $objectVar —>+———+
// |(handle1)—-+
// $reference —>+———+ |
// |
// +———+ |
// $assignment —>|(handle1)—-+
// +———+ |
// |
// v
// Object(1):foo=»bar»
//
?>
$assignment has a different data slot from $objectVar, but its data slot holds a handle to the same object. This makes it behave in some ways like a reference. If you use the variable $objectVar to change the state of the Object instance, those changes also show up under $assignment, because it is pointing at that same Object instance.
$objectVar -> foo = «qux» ;
print_r ( $objectVar );
print_r ( $reference );
print_r ( $assignment );
//
// $objectVar —>+———+
// |(handle1)—-+
// $reference —>+———+ |
// |
// +———+ |
// $assignment —>|(handle1)—-+
// +———+ |
// |
// v
// Object(1):foo=»qux»
//
?>
But it is not exactly the same as a reference. If you null out $objectVar, you replace the handle in its data slot with NULL. This means that $reference, which points at the same data slot, will also be NULL. But $assignment, which is a different data slot, will still hold its copy of the handle to the Object instance, so it will not be NULL.
$objectVar = null ;
print_r ( $objectVar );
print_r ( $reference );
print_r ( $assignment );