Перейти к содержимому

Как создать объект в php

  • автор:

«Правильное» создание объекта на php?

У меня несколько вопросов по объектам. В пхп при создании объект уже имеет значение не null. Поэтому, собственно, первый вопрос — как правильно (по хорошему тону) просигнализировать что при создании объекта произошли ошибка: бросить исключение с именем ошибки? Получается что любое неудачное действие метода должно сопровождаться брошенным исключением чтобы было ясно, что что-то пошло не так? И еще, перед выполнением какого-то метод объекта можно ли перед каждым эти методом с помощью средств самого пхп вызывать повторяющийся код (например создание соединения с бд, очистка содержимого свойств с именем и кодом ошибки) или для этого нужно просто создать свой внутренний метод и вызывать его по мере необходимости (опять, же, если следовать хорошему тону). Спасибо!

Отслеживать
задан 28 апр 2016 в 8:02
user3416803 user3416803
332 1 1 серебряный знак 8 8 бронзовых знаков

4 ответа 4

Сортировка: Сброс на вариант по умолчанию

Ошибок при создании объекта, можно избежать, явно указывая тип входных параметров в конструкторе. По хорошему, результат создания экземпляра класса не должен зависеть ни от каких других факторов кроме переданных в конструктор аргументов. Если же на вход приходит, к примеру, объект верного типа, но не верно сконфигурированный, правильно будет выбросить исключение, и оборачивать код создания объекта в блоки try-catch. Второй вариант, возложить ответственность за создание таких объектов на некий третий объект «фабрику», который будет проверять конфигурацию входных параметров, и, в случае не удовлетворения условиям, выбрасывать исключение, а в случае, если все условия соблюденны, создавать экземпляр искомого класса. По поводу второй части вопроса, есть такие варианты:

  1. вызывать внутренние методы по мере необходимости, их так же можно вынести в «супер-тип слоя» — базовый абстрактный класс иерархии, если эти методы общие для всех классов в иерархии.
  2. Заморочиться с магическими методами ( __call , __callStatic ) — http://php.net/manual/ru/language.oop5.overloading.php .
  3. Вас может заинтересовать аспектно-ориентированный подход — https://ru.wikipedia.org/wiki/%D0%90%D1%81%D0%BF%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5. Вот фреймворк, который достойно реализует это на PHP — https://github.com/goaop/framework, советую ознакомиться.

Отслеживать
ответ дан 28 апр 2016 в 8:30
Igor Karpenko Igor Karpenko
1,640 11 11 серебряных знаков 14 14 бронзовых знаков

Спасибо за линки, посмотрю. По поводу первого вопроса — у меня проблемный вопрос не правильность параметров, а результат работы функций подключения к БД, фтп и другим сервисам. То есть пользователь передает объекту хост и данные для входа на нужный сервис, и в зависимости от того удачное ли подключение или нет — создается объект, который с этим сервисом работает. Я так понял, что, наверное, исключения таки будет правильнее всего использовать.

28 апр 2016 в 8:35

такие блоки в любом случае должны использовать исключения (либо каким то другим образом обрабатывать исключительные ситуации, во времена PHP4 перебивались объектами PEAR_ERROR , имея на данный момент реализованный механизм исключений в самом языке, не вижу смысла, придумывать что-то другое).

28 апр 2016 в 8:39
Я понял, спасибо.
28 апр 2016 в 8:42

и оборачивать код создания объекта в блоки try-catch — нет, подобные случаи являются ошибкой программиста и не должны отлавливаться. Если код неправильно используется, проще сразу прекратить его работу, нежели пытаться продолжить.

28 апр 2016 в 11:10

В целом соглашусь, спасибо за замечание. По хорошему вообще не давать возможность создать не верно сконфигурированный объект, или, что даже более частый случай, не давать создать не полностью сконфигурированный объект, избегать т.н. temporal coupling. Т.е., если для корректной работы метода someMethod экземпляра класса aClass необходимым условием является проинициализированное поле aClass::strategy объектом aStrategy, то класс aClass должен ожидать объект типа aStrategy в конструкторе.

28 апр 2016 в 11:23

  1. Можно заюзать: Исключения И сделать обработку исключений. С помощью наследования, можно создать свои обработчики.
  2. Вызывать метод когда нужно, в котором уже ранее описаны действия для выполнения, чтобы избежать повторов в теле метода, есть замечательный модификатор static который может сохранить нужные нам данные после первой инициализации метода. function name($str) < static $name = null; if(!$name) < $name = $str; >return $name; > echo name(1); // 1 echo name(2); // 1

Отслеживать
ответ дан 28 апр 2016 в 8:19
4,086 4 4 золотых знака 13 13 серебряных знаков 29 29 бронзовых знаков

По поводу второго пункта спасибо, хороший совет со статической переменной. А по поводу первого: то есть каждый раз, когда что-то не так выбрасывать исключение? Или в самом объекте устанавливать/менять какие-то свойства, чтобы было ясно что произошла ошибка?

28 апр 2016 в 8:24

Отвратительный модификатор static. Теперь с чем бы вы ни вызвали тот метод, возвращать он все равно будет одно и то же даже с другими экземплярами класса.

28 апр 2016 в 9:49

Немного дополню отмеченный правильным ответ.

1.Создавать объекты, как было сказано выше, можно по-разному, но, вообще, желательно всё оборачивать в блоки try-catch и на разных уровнях либо обрабатывать исключение, либо «проталкивать» выше. Пример:

some_private_method(); > catch (Exception $e) < echo "Обработаем исключение в методе (getMessage()>)"; > > /** * "Проталкивание" исключения выше * @throws Exception */ public function some_public_method2() < try < $this->some_private_method(); > catch (Exception $e) < throw $e; >> private function some_private_method() < throw new Exception("Произошла ужасная ошибка"); >> try < $obj = new MyClass(); $obj->some_public_method(); echo "\n"; $obj->some_public_method2(); > catch (Exception $e) < echo "Обработаем исключение снаружи (getMessage()>)"; > ?> 

2.«а можно ли перед каждым эти методом с помощью средств самого пхп вызывать повторяющийся код». Да, можно! Есть множество вариантов, например:

Вариант 1 (вызывать нужный код в начале исполняемого метода):

какой_нибудь_метод_этого_класса //Либо просто какой-нибудь php-код //Возможно создание объекта другого класса и работа с ним //и что угодно echo "Что-то дальше делаем"; > > ?> 

Но, на мой взгляд, это практически всегда плохое решение, т.к. объект не должен знать «cлишком много». Почитайте про SOLID. Так что для решения этой задачи можно (и нужно) ипользовать какой-нибудь другой класс.

Вариант 2 («Оборачиваем» объект в другой объект):

 > class MyClassWrapper < public function get_my_class_some_method_with_additional_code() < /** * Какой угодно код */ $obj = new MyClass(); $obj->some_public_method(); > > ?> 

Вариант 3: Есть такой паттерн «Внедрение зависимостей (Dependency Injection)». Почитайте о нем, в интернет полно информации на русском языке, поэтому даже не знаю какую ссылку Вам привести.

Отслеживать
ответ дан 28 апр 2016 в 9:20
463 2 2 серебряных знака 5 5 бронзовых знаков
Спасибо за инфу, буду разбираться.
28 апр 2016 в 9:26

Поэтому, собственно, первый вопрос — как правильно (по хорошему тону) просигнализировать что при создании объекта произошли ошибка: бросить исключение с именем ошибки?

Верным ответом здесь будет «конструктор не должен быть построен таким образом, что в нем может возникнуть ошибка». В конструкторе не должно быть никакой логики, создание объекта само по себе должно быть бесплатным, а все тяжелые инициализационные процессы должны вызываться отдельными методами; зона ответственности конструктора — присвоение аргументов свойствам объекта. Если вы прячете что-то в конструктор, это а) обязательно выполнится и б) будет скрыто для модификации, поэтому если у вас внутри есть ошибка в логике, исправить ее будет невозможно.

В случае, если у вас не ошибка как таковая, а ошибка валидации, то следует бросать InvalidArgumentException, которые де-факто является флагом программисту о том, что он использует предоставленный ему код неправильно, передавая неверные значения.

Получается что любое неудачное действие метода должно сопровождаться брошенным исключением чтобы было ясно, что что-то пошло не так?

Это зависит от степени ответственности кода и непосредственно его предназначения. У кода либо есть ответственность за обработку некоторых исключений (e.g. менеджер подключений БД имеет право записать в соседнюю БД, если основная вдруг упала, и таким образом обработать исключение) или нет (класс подключения БД не имеет права скрыть информацию о том, что запись не прошла, однако имеет право словить ошибку/исключение и обернуть в исключение своего типа). В то же время ошибка может быть ожидаемой (e.g. выполнение запроса SELECT 1 для проверки подключения к базе в виде метода isAlive() ), в этом случае нужно вернуть соответствующее значение (false) вместо выброса исключения.

В любом случае при написании метода вам нужно спрашивать самого себя: Должен ли этот участок кода выбрасывать исключение? Должен ли он передавать информацию об ошибке выше?. Несмотря на то, что я возвращаю вам ваш же вопрос, иначе определить необходимость выброса исключения просто невозможно.

И еще, перед выполнением какого-то метод объекта можно ли перед каждым эти методом с помощью средств самого пхп вызывать повторяющийся код (например создание соединения с бд, очистка содержимого свойств с именем и кодом ошибки) или для этого нужно просто создать свой внутренний метод и вызывать его по мере необходимости

Про AOP уже ответили; я же просто дополню, что AOP хорош для задач своей специфики (e.g. профилирование методов), а в большинстве случаев стоит просто объявить метод ensureConnected() или аналог и вызывать его. Еще не могу не отметить, что эти примеры

например создание соединения с бд, очистка содержимого свойств с именем и кодом ошибки

являются дикими антипаттернами. Соединение с БД должно создаваться один раз, управление ошибками должно быть только через исключения.

Объектно-ориентированный PHP: подробнее о методах и полях класса

Добро пожаловать во второй урок из серии, посвященной ООП. В первой статье вы ознакомились с основами ООП в PHP, включая понятия классов, методов, полей и объектов. Также вы узнали, как создать простенький класс и реализовать его.

В данной статье вы узнаете еще больше о методах и полях класса. Это даст вам хорошую основу, для того чтобы приступить к изучению более профессиональных техник, таких как наследование.

Вот список того, о чем я расскажу вам в этой статье:

  • Конструкторы и деструкторы, которые позволяют назначить определенные действия объекту при его создании и удалении;
  • Статические поля и методы — это такие поля и методы, которые не связаны с конкретными объектами класса;
  • Константы класса, удобные для хранения фиксированных значений, относящихся к определенному классу;
  • Явное указание типа, используемое для задания ограничения типов параметров, которые можно передавать в тот или иной метод;
  • Специальные методы __get() и __set(), которые используются для задания и чтения значений полей классов;
  • Специальный метод __call(), применяемый для вызова метода класса.

Вы готовы? Тогда вперед!

Конструкторы и деструкторы

Иногда возникает необходимость выполнять какие-то действия одновременно с созданием объекта. Например, вам может понадобиться задать значения полям объекта сразу по его созданию, или же инициализировать их значениями из базы данных.

Подобно этому, вам также может понадобиться выполнять определенные действия по удалению объекта из памяти, например, удаление объектов, зависящих от удаляемого, закрытие соединения с базой данных или файлов.

На заметку: как удалить объект? PHP автоматически удаляет объект из памяти, когда не остается ни одной переменной, указывающей на него. Например, если вы создадите новый объект и сохраните его в переменной $myObject, а затем удалите ее с помощью метода unset($myObject), то сам объект также удалится. Также, если вы создали локальную переменную в какой-либо функции, она (вместе с объектом) удалится, когда функция завершит работу.

В PHP есть два специальных метода, которые можно применять для совершения определенных действий по созданию и удалению объектов:

  • Конструктор вызывается сразу после того, как вы создали объект;
  • Деструктор вызывается строго перед тем, как объект удаляется из памяти.

Работа с конструкторами

Применяйте конструкторы, чтобы задать действия, которые будут выполняться по созданию объекта класса. Эти действия могут включать инициализацию полей класса, открытие файлов, чтение данных.

Чтобы создать конструктор, добавьте в ваш класс специальный метод __construct() (перед словом construct — два символа подчеркивания). PHP автоматически вызовет этот метод при реализации вашего класса, то есть, при создании объекта этого класса.

Вот пример конструктора:

class MyClass < public function __construct() < echo "I've just been created!"; >> $myObject = new MyClass(); // отобразит "I've just been created!"

В классе MyClass есть конструктор, который выводит на страницу строку «I’ve just been created!». Последняя строка кода создает новый объект класса MyClass. Когда это происходит, PHP автоматически вызывает конструктор, и сообщение отображается в браузере. Теперь на практике — инициализация полей класса:

class Member < private $username; private $location; private $homepage; public function __construct( $username, $location, $homepage ) < $this->username = $username; $this->location = $location; $this->homepage = $homepage; > public function showProfile() < echo "
"; echo "
Username:
$this->username
"; echo "
Location:
$this->location
"; echo "
Homepage:
$this->homepage
"; echo "
"; > > $aMember = new Member( "fred", "Chicago", "http://example.com/" ); $aMember->showProfile();

Данный скрипт отобразит на странице следующее:

Username: fred Location: Chicago Homepage: http://example.com/

В нашем классе Member есть три поля и конструктор, который принимает в качестве параметров 3 значения — по одному для каждого поля. Конструктор назначит полям объекта значения, полученные в качестве аргументов. В классе также есть метод для отображения на странице значений полей объекта.

Затем в коде создается объект класса Member, в который мы передаем 3 значения «fred», «Chicago», и «http://example.com/», так как конструктор принимает именно 3 параметра. Конструктор записывает эти значения в поля созданного объекта. В завершение, вызывается метод showProfile() для созданного объекта, чтобы отобразить полученные значения.

Работа с деструкторами

Применяйте деструктор, когда объект удаляется из памяти. Вам может понадобиться сохранить объект в базе данных, закрыть открытые файлы, которые взаимодействовали с объектом. Чтобы создать деструктор, добавьте в класс метод __destruct(). Он вызовется как раз перед удалением объекта автоматически. Вот простой пример:

class MyClass < public function __destruct() < echo "I'm about to disappear - bye bye!"; // (очистить память) >> $myObject = new MyClass(); unset( $myObject ); // отобразит "I'm about to disappear - bye bye!"

Мы создали простенький деструктор, который отображает на странице сообщение. Затем мы создали объект нашего класса и сразу же удалили его, вызвав метод unset() для переменной, которая ссылается на объект. Перед самым удалением объекта вызвался деструктор, который отобразил в браузере сообщение «I’m about to disappear — bye bye!».

На заметку: в отличие от конструкторов, в деструкторы нельзя передавать никакие параметры.

Деструктор также вызывается при выходе из скрипта, так как все объекты и переменные при выходе из метода удаляются. Так, следующий код также вызовет деструктор:

class MyClass < public function __destruct() < echo "I'm about to disappear - bye bye!"; // (очистить память) >> $myObject = new MyClass(); exit; // отобразит "I'm about to disappear - bye bye!"

Также, если работа скрипта прекратится из-за возникшей ошибки, деструктор тоже вызовется.

На заметку: при создании объектов класса-наследника, конструкторы класса-родителя не вызываются автоматически. Вызывается только конструктор самого наследника. Тем не менее вы можете вызвать конструктор родителя из класса-наследника таким образом:

parent::__construct(). То же самое касается деструкторов. Вызвать деструктор родителя можно так: parent:__destruct(). Я расскажу вам о классах-родителях и наследниках в следующем уроке, посвященном наследованию.

Статические поля класса

Мы рассмотрели статические переменные в статье PHP Variable Scope: All You Need to Know. Как обычная локальная переменная, статическая переменная доступна только в пределах функции. Тем не менее, в отличие от обычных локальных, статические переменные сохраняют значения между вызовами функции.

Статические поля класса работают по такому же принципу. Статическое поле класса связано со своим классом, однако оно сохраняет свое значение на протяжении всей работы скрипта. Сравните это с обычными полями: они связаны с определенным объектом, и они теряются при удалении этого объекта.

Статические поля полезны в случаях, когда вам нужно хранить определенное значение, относящееся ко всему классу, а не к отдельному объекту. Они похожи на глобальные переменные класса.

Чтобы создать статическую переменную, добавьте ключевое слово static в ее задании:

class MyClass

Вот пример того, как работают статические переменные:

class Member < private $username; public static $numMembers = 0; public function __construct( $username ) < $this->username = $username; self::$numMembers++; > > echo Member::$numMembers . "
"; // отобразит "0" $aMember = new Member( "fred" ); echo Member::$numMembers . "
"; // отобразит "1" $anotherMember = new Member( "mary" ); echo Member::$numMembers . "
"; // отобразит "2"

Есть несколько интересных вещей, так что давайте разберем данный скрипт:

  • В классе Member два поля: частное поле $username и статическое $numMembers, которое изначально получает значение 0;
  • Конструктор получает в качестве параметра аргумент $username и устанавливает полю только что созданного объекта значение этого параметра. В то же время, он инкрементирует значение поля $numMembers, тем самым давая понять, что число объектов нашего класса увеличилось на 1.

Отметьте, что конструктор обращается к статическому полю так: self::$numMembers. Ключевое слово self похоже на $this, которое мы рассмотрели в прошлом уроке. Тогда как $this ссылается на текущий объект, self — на текущий класс. Также тогда как для получения доступа к полям и методам объекта вы используете ->, то в этом случае используйте :: для получения доступа к полям и методам класса.

  • В завершении скрипт создает несколько объектов класса Member и отображает на странице их количество, т.е. значение статической переменной $numMembers. Отметьте, что данная переменная сохраняет свое значение на протяжении всей работы скрипта, несмотря на объекты класса.

Итак, чтобы получить доступ к статическому полю класса, применяйте оператор . Здесь мы не можем воспользоваться ключевым словом self, так как код находится за пределами класса, поэтому мы пишем имя класса, затем . а затем имя поля (Member::$numMembers). В пределах конструктора тоже нужно использовать именно такую структуру, а не self.

На заметку: нашему скрипту ничего не стоило получить доступ к полю класса $numMembers перед тем, как создался первый объект данного класса. Нет необходимости создавать объекты класса для того, чтобы пользоваться его статическими полями.

Статические методы

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

Чтобы создать статический метод, нужно добавить в его объявлении ключевое слово static:

class MyClass < public static function myMethod() < // (действия) >>

В нашем предыдущем примере, касающемся статических полей, было статическое поле $numMembers. Делать поля частными, а методы для доступа к ним — открытыми, — это хорошая практика. Давайте сделаем наше статическое поле частным и напишем статический метод public для получения значения данного поля:

class Member < private $username; private static $numMembers = 0; public function __construct( $username ) < $this->username = $username; self::$numMembers++; > public static function getNumMembers() < return self::$numMembers; >> echo Member::getNumMembers() . "
"; // отобразит "0" $aMember = new Member( "fred" ); echo Member::getNumMembers() . "
"; // отобразит "1" $anotherMember = new Member( "mary" ); echo Member::getNumMembers() . "
"; // отобразит "2"

Здесь мы создали статический метод getNumMembers(), который возвращает значение статического поля $numMembers. Мы также сделали это поле частным, чтобы нельзя было получить его значение извне.

Мы также изменили код вызова, применив метод getNumMembers() для получения значения поля $numMembers. Отметьте, можно вызывать данный метод без того, чтобы создавать объект класса, потому что метод — статический.

Константы класса

Константы позволяют задать глобальное значение для всего вашего кода. Это значение фиксированное, оно не может быть изменено. Константы класса схожи с обычными константами. Основное их отличие заключается в том, что помимо того, что классовая константа глобальна, к ней можно получить доступ из класса, в котором она определена. Классовые константы полезны в случаях, когда вам нужно хранить определенные значения, которые относятся к определенному классу.

Определить классовую константу можно с помощью ключевого слова const. Например:

class MyClass

Обратиться в последствии к классовой константе можно через имя класса и оператор . Например, так:

MyClass::CONSTANT_NAME

На заметку: как и в случае со статическими полями и методами, вы можете обратиться к константе через ключевое слово self.

Давайте рассмотрим классовые константы на примере. Добавим в класс Member константы, в которых будут храниться значения их роли (участник, модератор или администратор). Применив константы вместо обычных численных значений, мы сделали код более читабельным. Вот скрипт:

class Member < const MEMBER = 1; const MODERATOR = 2; const ADMINISTRATOR = 3; private $username; private $level; public function __construct( $username, $level ) < $this->username = $username; $this->level = $level; > public function getUsername() < return $this->username; > public function getLevel() < if ( $this->level == self::MEMBER ) return "a member"; if ( $this->level == self::MODERATOR ) return "a moderator"; if ( $this->level == self::ADMINISTRATOR ) return "an administrator"; return "unknown"; > > $aMember = new Member( "fred", Member::MEMBER ); $anotherMember = new Member( "mary", Member::ADMINISTRATOR ); echo $aMember->getUsername() . " is " . $aMember->getLevel() . "
"; // отобразит "fred is a member" echo $anotherMember->getUsername() . " is " . $anotherMember->getLevel() . "
"; // отобразит "mary is an administrator"

Мы создали три классовые константы: MEMBER, MODERATOR и ADMINISTRATOR, и задали им значения 1, 2 и 3 соответственно. Затем мы добавляем поле $level для хранения ролей и немного изменяем конструктор так, чтобы инициализировать еще и это поле. В классе также появился еще один метод — getLevel(), который возвращает определенное сообщение в зависимости от значения поля $level. Он сравнивает это значение с каждой из классовых констант и возвращает нужную строку.

Скрипт создает несколько объектов с разными ролями. Для задания объектам ролей используются именно классовые константы, а не простые численные значения. Затем идут вызовы методов getUsername() и getLevel() для каждого объекта, и результаты отображаются на странице.

Явное указание типов аргументов функций

В PHP можно не задавать типы данных, так что можно не переживать о том, какие аргументы вы передаете в методы. Например, вы можете спокойно передать в функцию strlen(), считающую длину строки, численное значение. PHP сперва переведет число в строку, а затем вернет ее длину:

echo strlen( 123 ); // отобразит"3"

Иногда явное указание типа полезно, но оно может привести к багам, с которыми трудно будет справиться, особенно в случае, если вы работаете с такими сложными типами данных, как объекты.

Например

Посмотрите на этот код:

class Member < private $username; public function __construct( $username ) < $this->username = $username; > public function getUsername() < return $this->username; > > class Topic < private $member; private $subject; public function __construct( $member, $subject ) < $this->member = $member; $this->subject = $subject; > public function getUsername() < return $this->member->getUsername(); > > $aMember = new Member( "fred" ); $aTopic = new Topic( $aMember, "Hello everybody!" ); echo $aTopic->getUsername(); // отобразит "fred"

Данный скрипт работает так:

  • Мы создаем наш класс Member с полем $username, конструктором и методом getUsername();
  • Также создаем класс Topic для управления статьями форума. У него два поля: $member и $subject. $member — это объект класса Member, это будет автор статьи. Поле $subject — это тема статьи.
  • В классе Topic также содержится конструктор, который принимает объект класса Member и строку — тему статьи. Этими значениями он инициализирует поля класса. У него еще есть метод getUsername(), который возвращает имя участника форума. Это достигается через вызов метода getUsername() объекта Member.
  • В завершении создаем объект класса Member со значением поля username “fred”. Затем создаем объект класса Topic, передав ему Фреда и тему статьи “Hello everybody!”. В конце вызываем метод getUsername() класса Topic и отображаем на странице имя пользователя (“fred”).

Это все очень хорошо, но.

Давайте сделаем лучше!

Добавим этот фрагмент кода в конце:

class Widget < private $colour; public function __construct( $colour ) < $this->colour = $colour; > public function getColour() < return $this->colour; > > $aWidget = new Widget( "blue" ); $anotherTopic = new Topic( $aWidget, "Oops!" ); // отобразит "Fatal error: Call to undefined method Widget::getUsername()" echo $anotherTopic->getUsername();

Здесь мы создаем класс Widget с полем $colour, конструктором и методом getColour(), который возвращает цвет виджета.

Затем мы создадим объект данного класса, а за ним объект Topic с аргументом $aWidget, когда на самом деле нужно передавать автора статьи, т.е. объект класса Member.

Теперь попытаемся вызвать метод getUsername() класса Topic. Этот метод обращается к методу getUsername() класса Widget. И так как в этом классе нет такого метода, мы получаем ошибку:

Fatal error: Call to undefined method Widget::getUsername()

Проблема в том, что причина ошибки не так легко уяснима. Почему объект Topic ищет метод в классе Widget, а не Member? В сложной иерархии классов будет очень сложно найти выход из такого рода ситуации.

Даем подсказку

Было бы лучше ограничить конструктор класса Topic на прием аргументов так, чтобы он мог принимать в качестве первого параметра объекты только класса Member, тем самым предостеречься от фатальных ошибок.

Это как раз то, чем занимается явное указание типов. Чтобы явно указать тип параметра, вставьте имя класса перед названием аргумента в объявлении метода:

function myMethod( ClassName $object ) < // (действия) >

Давайте подкорректируем конструктор класса Topic так, чтобы он принимал только Member:

class Topic < private $member; private $subject; public function __construct( Member $member, $subject ) < $this->member = $member; $this->subject = $subject; > public function getUsername() < return $this->member->getUsername(); > >

Теперь снова попытаемся создать объект Topic, передав ему Widget:

$aWidget = new Widget( "blue" ); $anotherTopic = new Topic( $aWidget, "Oops!" );

На этот раз PHP отобразит конкретную ошибку:

Catchable fatal error: Argument 1 passed to Topic::__construct() must be an instance of Member, instance of Widget given, called in script.php on line 55 and defined in script.php on line 24

С этой проблемой будет намного легче справиться, так как мы точно знаем, в чем именно заключается причина ошибки — мы попытались передать в конструктор параметр не того типа, который нужен. В сообщении об ошибке даже точно указаны строчки кода, где был вызван метод, ставший ее причиной.

Инициализация и чтение значений полей класса при помощи __get() и __set()

Как вы уже знаете, классы обычно содержат поля:

class MyClass

Если поля класса — public, вы можете получить к ним доступ с помощью оператора ->:

$myObject = new MyClass; $myObject->aProperty = "hello";

Тем не менее, PHP позволяет создавать “виртуальные” поля, которых на самом деле нет в классе, но к которым можно получить доступ через оператор ->. Они могут быть полезны в таких случаях:

  • Когда у вас очень много полей, и вы хотите создать для них массив, чтобы не объявлять каждое поле отдельно;
  • Когда вам нужно хранить поле за пределами объекта, например, в другом объекте, или даже в файле или базе данных;
  • Когда вам нужно вычислять значения полей “на лету”, а не хранить их значения где-либо.

Чтобы создать такие “виртуальные” поля, нужно добавить в класс парочку волшебных методов:

  • __get( $propName ) вызывается автоматически при попытке прочитать значение “невидимого” поля $propName;
  • __set( $propName,$propValue ) вызывается автоматически при попытке задать “невидимому” полю $propName значение $propValue.

“Невидимый” в данном контексте значит, что на данном участке кода нельзя прямо получить доступ к данным полям. Например, если такого поля вообще нет в классе, или если оно существует, но оно частное, и за пределами класса нет доступа к такому полю.

Перейдем к практике. Изменим наш класс Member так, чтобы в дополнение полю $username были еще и другие случайные поля, которые будут храниться в массиве $data:

class Member < private $username; private $data = array(); public function __get( $property ) < if ( $property == "username" ) < return $this->username; > else < if ( array_key_exists( $property, $this->data ) ) < return $this->data[$property]; > else < return null; >> > public function __set( $property, $value ) < if ( $property == "username" ) < $this->username = $value; > else < $this->data[$property] = $value; > > > $aMember = new Member(); $aMember->username = "fred"; $aMember->location = "San Francisco"; echo $aMember->username . "
"; // отобразит "fred" echo $aMember->location . "
"; // отобразит "San Francisco"

Вот, как это работает:

  • В классе Member есть постоянное поле private $username и private массив $data для хранения случайных “виртуальных” полей;
  • Метод __get() принимает единственный параметр $property — имя поля, значение которого нужно вернуть. Если $property = “username”, то метод вернет значение поля $username. В другом случае, метод проверит, встречается ли такой $property в ключах массива $data. Если найдется такой ключ, он вернет значение данного поля, в противном случае — null.
  • Метод __set() принимает 2 параметра: $property — имя поля, которое нужно инициализировать, и $value — значение, которое нужно задать данному полю. Если $property = “username”, метод инициализирует поле $username значением из параметра $value. В противном случае, добавляет в массив $data ключ $property со значением $value.
  • После создания класса Member, создаем объект этого класса и инициализируем его поле $username значением “fred”. Это вызывает метод __set(), который задаст значение $username объекту. Затем устанавливаем значение поля $location в “San Francisco”. Так как такого поля не существует в объекте, метод записывает его в массив $data.
  • В конце, достаем значения $username и $location и выводим их на страницу. Метод __get() достает действительное значение $username из существующего поля $username, а значение $location — из массива $data.

Как видите, с помощью методов __get() и __set() мы создали класс, в котором могут быть как настоящие поля, так и любые “виртуальные”. Из фрагмента кода, где задается значение тому или иному полю, не обязательно знать, существует ли такое поле или нет в объекте. Через обычный оператор -> можно задать полю значение или прочитать его.

В примере также показано, как можно легко создать методы, называемые “геттерами” и “сеттерами”, для доступа к частным полям. Нам не нужно создавать отдельные методы getUsername() и setUsername() для получения доступа к частному полю $username. Вместо этого мы создали методы __get() и __set() для манипулирования данным полем. Это значит, что нам нужно всего 2 метода в общем, а не по 2 метода для каждого частного поля.

На заметку: Слово о инкапсуляции. Использование частных полей класса в комбинации с геттерами и сеттерами — это лучше, чем использование переменных public. Геттеры и сеттеры дополнительно могут обрабатывать данные, задаваемые полям объекта и получаемые из них, например, проверять, в правильном ли формате находится значение, или конвертировать его в нужный формат. Геттеры и сеттеры также скрывают детали того, как имплементируются поля класса, что упрощает процесс модификации внутренней части класса, так как не нужно переписывать код, который оперирует объектами данного класса. Например, вы вдруг захотели хранить значение поля в базе данных. Если у вас уже были геттеры и сеттеры, все, что вам нужно, — это переписать их. А вызывающий код останется таким же. Эта техника называется инкапсуляцией, и это одно из главных преимуществ ООП.

Перегрузка методов с помощью __call()

Геттеры и сеттеры используются для запрета на доступ к частным переменным. В этом же направлении используется метод __call() для запрета доступа к частным методам. Как только из кода вызывается метод класса, который либо не существует, либо он недоступен, автоматически вызывается метод __call(). Вот общий синтаксис метода:

public function __call( $methodName, $arguments ) < // (действия) >

Когда производится попытка вызвать недоступный метод класса, PHP автоматически вызывает метод __call(), в который передает строку — имя вызываемого метода и список передаваемых параметров в массиве. Затем ваш метод __call() должен будет определенным способом обработать вызов и, в случае необходимости, вернуть значения.

Метод __call() полезен в ситуациях, когда вам нужно передать некую функциональность класса другому классу. Вот простой пример:

class Member < private $username; public function __construct( $username ) < $this->username = $username; > public function getUsername() < return $this->username; > > class Topic < private $member; private $subject; public function __construct( $member, $subject ) < $this->member = $member; $this->subject = $subject; > public function getSubject() < return $this->subject; > public function __call( $method, $arguments ) < return $this->member->$method( $arguments ); > > $aMember = new Member( "fred" ); $aTopic = new Topic( $aMember, "Hello everybody!" ); echo $aTopic->getSubject() . "
"; // отобразит "Hello everybody!" echo $aTopic->getUsername() . "
"; // отобразит "fred"

Данный пример похож на тот, что приводился в разделе о явном указании типов. У нас есть класс Member с полем $username и класс Topic с полем — объектом класса Member (автор статьи) и полем $subject — темой статьи. Класс Topic содержит метод getSubject() для получения темы статьи, но в нем нет метода, который возвращал бы имя автора статьи. Вместо него в нем есть метод __call(), который вызывает несуществующий метод и передает аргументы методу класса Member.

Когда в коде вызывается метод $aTopic->getUsername(), PHP понимает, что такого метода в классе Topic не существует. Поэтому вызывается метод __call(), который в свою очередь, вызывает метод getUsername() класса Member. Этот метод возвращает имя автора методу __call(), а тот отправляет полученное значение вызывающему коду.

На заметку: в PHP есть и другие методы, касающиеся перегрузки, например, __isset(), __unset(), и __callStatic().

Заключение

В этом уроке вы углубили свои знания по ООП в PHP, рассмотрев более детально поля и методы. Вы изучили:

  • Конструкторы и деструкторы, полезные для инициализации полей и очистки памяти после удаления объектов;
  • Статические поля и методы, которые работают на уровне класса, а не на уровне объекта;
  • Классовые константы, полезные для хранения фиксированных значений, необходимых на уровне класса;
  • Явное указание типов, с помощью которого можно лимитировать типы аргументов, передаваемых в метод;
  • Волшебные методы __get(), __set() и __call(), которые служат для получения доступа к частным полям и методам класса. Реализация этих методов позволяет вам создавать “виртуальные” поля и методы, которые не существуют в классе, но в то же время, могут быть вызваны.

Со знаниями, полученными в этом и предыдущем уроках, вы можете начать писать на ООП. Но на этом все только начинается! В следующем уроке мы поговорим о силе ООП — способности классов наследовать функциональность от других классов.

Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: www.elated.com/articles/object-oriented-php-delving-deeper-into-properties-and-methods/
Перевел: Станислав Протасевич
Урок создан: 27 Июня 2011
Просмотров: 112646
Правила перепечатки

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 сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.

PHP: как создать объект по имени класса

Федор К.

Суть проблемы.
Есть некий класс. Имя которого задается в строковой переменной, а сам класс инклайдиться.
Как создать объект этого класса.

и теперь хочу получить объект. Как?

Евгений Кашкаров

450 сообщений
# 13 лет назад
Во-первых, исправьте

nclude $class . ".php";

и попробуйте

$ob = new MyClass;

Евгений Кашкаров

450 сообщений
# 13 лет назад
Перечитал ещё раз, я вас неправильно понял. Попробуйте

eval('$ob = new '.$class.';');

Владимир Орлов

206 сообщений
# 13 лет назад

1)
$obj = new $className();

2)
$class = new ReflectionClass($className);
$obj = $class->newInstance();

3)
$class = new ReflectionClass($className);
$obj = call_user_func_array(array(&$class, ‘newInstance&#039, $args);//$args = массив аргументов

где-то на php.net есть сравнеие быстродействия. 2-й способ быстрее.

Николай Т.

205 сообщений
# 13 лет назад

 
$class = new ReflectionClass($Name);
if ($class->getConstructor() === NULL)
$object = new $Name();
>
else
$object = $class->newInstanceArgs(array('arg1'=>1, 'arg2'=>2));
>

единственный верный вариант работает от 5.1.3

Алексей Лебедев

49 сообщений
# 13 лет назад

e1it3: всего то достаточно объявлять в каждом классе конструктор по умолчанию, по-моему, это лучше чем использовать подобные конструкции.

Используйте первый способ как самый читабельный.

Вариант с отражением удобен когда перед созданием нужно проверить реализует ли класс требуемый интерфейс:

 
$command = null;
// .
$class = 'OtherClass';
$commandClass = new ReflectionClass($class);
if ($commandClass->isSubClassOf('MyClass')) $command = $commandClass->newInstance();
>

Сравнивать скорости — пустая трата времени. Не могу представить ни одного приложения где создание объекта одним из предложенных способов станет узким местом.

И самый прозрачный вариант — использовать автозагрузку:

 
$obj = new MyObject();

function __autoload($className) require_once $className.'.php';
>

Create an Object Without Class in PHP

Create an Object Without Class in PHP

  1. Use new stdClass() to Create an Object Without a Class in PHP
  2. Typecast an Array Into an Object Using the object Data Type to Create an Object Without Creating a Class
  3. Use the json_dencode() Function to Create an Object Without Creating a Class in PHP

This article will introduce methods to create an object without creating a class in PHP.

Use new stdClass() to Create an Object Without a Class in PHP

We can create an object from the stdClass() without making a base class in PHP. We can use the new operator to create an object of the stdClass() . The object can access the properties directly by calling them. Thus, we can create dynamic objects and properties using the stdClass() . The compiler creates an instance of the stdClass() when an array is typecasted to an object.

For example, create a variable $object and store an instance of the stdClass() created by the new operator. Call a variable property from the $object object and assign a string. Assign the string, I am an object’s property . Use the backslash \ to escape the apostrophe in the text. Apply the var_dump() function on the $object variable to dump the information about the variable.

In the example below, we created an object of the stdClass() , and we instantly assigned a value to the object’s property. When we dump the object, we will see the object of the stdClass class created in the output. Thus, we can create an object without creating a base class in PHP.

#php 7.x  php $object = new stdClass(); $object->property = 'I am an object\'s property'; var_dump($object); ?> 
object(stdClass)#1 (1) < ["property"]=>string(25) "I am an object's property" > 

Typecast an Array Into an Object Using the object Data Type to Create an Object Without Creating a Class

We can create an object without creating a class in PHP, typecasting a type into an object using the object data type. We can typecast an array into a stdClass object. The object keyword is wrapped around with parenthesis right before the array typecasts the array into the object. We can use the var_dump() function as the first method to see the information about the typecasted object.

For example, create a variable $place and store an array in it. The array contains keys as city and country and the respective values as Pokhara and Nepal . Assign the $place variable to a new $obj variable. Write the object data type wrapped inside a parenthesis just before the $place variable. Call the var_dump() function with the $obj as parameter.

The example below typecasts an array into an object. The var_dump() function shows the $obj object in the output section. It shows that it is a stdClass object. Check the PHP Manual to know how typecasting works.

#php 7.x  php $place = ['city' => 'Pokhara', 'country' =>'Nepal']; $obj = (object) $place; var_dump($obj); ?> 
object(stdClass)#1 (2) < ["city"]=>string(7) "Pokhara" ["country"]=> string(5) "Nepal" > 

Use the json_dencode() Function to Create an Object Without Creating a Class in PHP

The json_decode() function converts the JSON string into a PHP object. The function takes a string parameter which will be converted into an object. The function also takes an optional boolean parameter. The default value of this boolean parameter is false , which will convert the JSON object into a PHP object. If the value is true , it will convert the JSON object into an associative array.

For example, create a variable $jsonobj and store a JSON object into it. The object contains Harry , Tony and Juan as the key and the integer values 27 , 24 and 32 as the respective values. The key-value pairs are enclosed within the curly braces. The JSON object is wrapped around with quotes as a string. Then use the json_decode() function on the $jsonobj variables. Apply the var_dump() function to the json_encode() function to display the information about the encoded object.

In the example below, json_encode() function converts the JSON object to PHP object of stdClass . The var_dump() function shows the property and the value of the object. Thus, we created an object without creating a class in PHP. It also displays the type of the value. Please check the PHP Manual to understand more about the json_decode() function.

# php 7.x  php $jsonobj = ''; var_dump(json_decode($jsonobj)); ?> 
object(stdClass)#1 (3) < ["Harry"]=>int(27) ["Tony"]=> int(24) ["Juan"]=> int(32) > 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Luxury Car Rental in Miami | Reserve a Premium Vehicle with RealCar