php приватная переменная в классе

Получаем доступ к приватным свойствам объектов в PHP без рефлексии

Несколько недель назад я работал над проблемой в ProxyManager. Проблема была проста: ReflectionClass и ReflectionProperty очень, очень, и ооочень медленные!
Причиной этого исследования является моя попытка оптимизировать «hydrator» для работы с большими объемами данных без накладных расходов на инициализацию.

PHP 5.4 выручай!

Определим замыкание для получения этого поля:

А теперь украдем yummy из экземпляра Kitchen :

Сделаем нашего вора умнее Closure#bind() :

Closure::bind vs Reflection: быстродействие

Я сделал простой бенчмарк для 100000 итераций инициализации:

На только что скомпилированном PHP 5.5 (Ubuntu 13.04 amd64 box), первый тест занял 0.325 секунд, а второй 0.658.

Рефлексия здесь гораздо медленнее.(На 49%)

Но это совсем не интересно, так как никому не потребуется инициализировать 100000 раз одно и то же, по крайней мере мне точно. Что на самом деле интересно — так это доступ к приватным свойствам. Я протестировал и это тоже:

0.110 секунд, а второй

0.199!
Это гораздо быстрее рефлексии, впечатляет!(На 55%)

Доступ к приватным свойствам по ссылкам

Есть еще одно преимущество, используя замыкания вместо рефлексии мы можем работать с свойствами объекта по ссылкам!

Универсальный метод доступа

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

Рабочий пример.
У нас есть доступ к любому свойству, в любом месте, и даже по ссылке. Ура! Мы нарушили правила еще раз!

Заключение

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

Дисклеймер: используйте данную возможность с осторожностью!

Источник

Область видимости

Область видимости свойства или метода может быть определена путем использования следующих ключевых слов в объявлении: public, protected или private. Доступ к свойствам и методам класса, объявленным как public (общедоступный), разрешен отовсюду. Модификатор protected (защищенный) разрешает доступ наследуемым и родительским классам. Модификатор private (закрытый) ограничивает область видимости так, что только класс, где объявлен сам элемент, имеет к нему доступ.

Область видимости свойства

Свойства класса должны быть определены через модификаторы public, private, или protected. Если же свойство определено с помощью var, то оно будет доступно как public свойство.

Пример #1 Объявление свойства класса

Область видимости метода

Методы класса должны быть определены через модификаторы public, private, или protected. Методы, где определение модификатора отсутствует, определяются как public.

Пример #2 Объявление метода

// Объявление общедоступного метода
public function MyPublic ()

// Объявление защищенного метода
protected function MyProtected ()

// Объявление закрытого метода
private function MyPrivate ()

public function testPublic () <
echo «Bar::testPublic\n» ;
>

private function testPrivate () <
echo «Bar::testPrivate\n» ;
>
>

class Foo extends Bar
<
public function testPublic () <
echo «Foo::testPublic\n» ;
>

private function testPrivate () <
echo «Foo::testPrivate\n» ;
>
>

Видимость из других объектов

Объекты одного типа имеют доступ к элементам с модификаторами private и protected друг друга, даже если не являются одним и тем же экземпляром. Это объясняется тем, что реализация видимости элементов известна внутри этих объектов.

Пример #3 Доступ к элементам с модификатором private из объектов одного типа

private function bar ()
<
echo ‘Доступ к закрытому методу.’ ;
>

$test = new Test ( ‘test’ );

Источник

PHP, статические переменные внутри методов класса и история одного бага

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

Итак, когда после обеда я подошёл к своему коллеге Роману parpalak, он как раз закончил приводить в порядок юнит-тесты, и запустил всю пачку. Один из тестов выкинул исключение и упал. Ага, подумали мы, сейчас исправим баг. Запустили тест в одиночестве, вне пакета, и он прошёл успешно.

Прежде чем сбросить с себя послеобеденную дремоту, мы запустили Codeception ещё несколько раз. В пакете тест падал, в одиночку проходил, в пакете падал…

Фаталка Call to private method вылетала из метода, преобразующего объект сущности в массив для отправки клиенту. Недавно механизм этого процесса немного изменился, но ещё не все классы отрефакторили, поэтому в методе стоит проверка, переопределён ли метод, возвращающий список необходимых полей (это старый способ), в дочернем классе. Если нет, список полей формируется через рефлексию (это новый способ), и вызываются соответствующие геттеры. В нашем случае один из геттеров был объявлен как private, и, соответственно, недоступен из базового класса. Всё это выглядит примерно так:

— А что, рефлексия такая тяжёлая операция? — спросил я.
— Ну да, — кивнул Роман.

— Как такое может быть? — спросил я. — Статическая переменная внутри метода шарится при наследовании? Почему тогда мы раньше этого не заметили?

Роман был озадачен не меньше моего. Пока я ходил за кофе, он набросал небольшой юнит-тест с имитацией нашей иерархии классов, но он не падал. Мы что-то упускали из виду. Статическая переменная вела себя неправильно, не так, как мы ожидали, но не во всех случаях, и мы не могли понять, почему. Гугление по запросу «php static variable inside class method» не давало ничего путного, кроме того, что статические переменные — это нехорошо. Well, duh!

Теперь Роман пошёл за кофе, а я в задумчивости открыл PHP-песочницу и написал самый простой код:

простой пример 1

простой пример 2

простой пример 3

Вот оно! Роман как раз вернулся, и я, довольный собой, продемонстрировал свои наработки. Ему понадобилось всего несколько нажатий на клавиатуру в PHPStorm, чтобы отрефакторить участок со статической переменной в отдельный метод:

простой пример 4

В итоге мы объявили метод hasOriginalClientProps как protected и снабдили пространным комментарием.

Анализ

Время не ждало, и мы перешли к дальнейшим задачам, но всё же такое поведение озадачивало. Я решил разобраться, почему же PHP ведёт себя именно таким образом. В документации не удалось нарыть ничего, кроме неясных намёков. Ниже я попробую восстановить картину происходящего, основываясь на вдумчивом чтении PHP Internals Book, PHP Wiki, изучении исходников и информации о том, как реализуются объекты в других языках программирования.

Лично мне такое копирование показалось довольно тяжеловесным. Видимо, разработчики PHP подумали так же и отказались от копирования для приватных методов. Ведь они же всё равно не видны из дочерних и родительских классов! Вон, мы даже фаталку в самом начале рассказа словили из-за этого. Поэтому приватный метод существует в единственном экземпляре по всей иерархии классов, и статические переменные в нём тоже существует в единственном контексте. Поэтому и не заработал пример 4.

Такое поведение повторяется на всех версиях PHP, которые я попробовал в песочнице, начиная с мохнатой 5.0.4.

Почему же баг в коде нашего проекта раньше никак не давал о себе знать? Видимо, сущности редко создавались разнотипными группами, а если и создавались — то рефакторили их одновременно. А вот при прогоне тестов в один запуск скрипта попали два объекта, работающие через разные механизмы, и один из них испортил другому состояние.

Выводы

(ведь в каждой серьёзной статье должны быть выводы)

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

Источник

PHP: Наследование

Наследование

Класс, который получается в результате наследования от другого, называется подклассом. Эту связь обычно описывают с помощью терминов «родительский» и «дочерний». Дочерний класс происходит от родительского и наследует его характеристики: свойства и методы. Обычно в подклассе к функциональности родительского класса (который также называют суперклассом) добавляются новые функциональные возможности.

Обратите внимание на то, что в подклассах могут переопределяться свойства и методы. Определяя подкласс, мы гарантируем, что его экземпляр определяется характеристиками сначала дочернего, а затем родительского класса. Чтобы лучше это понять рассмотрим пример:

Так как мы уже рассмотрели тему про указание типа аргументов, осталось сказать о том, что если в качестве типа указан родительский класс, то все потомки для метода будут так же доступны для использования, посмотрите на следующий пример:

Оператор parent

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

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

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

Ключевое слово parent можно использовать не только в конструкторах, но и в любом другом методе, функциональность которого вы хотите расширить, достигнуть этого можно, вызвав метод родительского класса:

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

Теперь, когда мы познакомились с основами наследования, можно, наконец, рассмотреть вопрос видимости свойств и методов.

public, protected и private: управление доступом

До этого момента мы явно объявляли все свойства как public (общедоступные). И такой тип доступа задан по умолчанию для всех методов.

Элементы класса можно объявлять как public (общедоступные), protected (защищенные) и private (закрытые). Рассмотрим разницу между ними:

Модификатор protected с точки зрения вызывающей программы выглядит точно так же, как и private : он запрещает доступ к данным объекта извне. Однако в отличие от private он позволяет обращаться к данным не только из методов своего класса, но также и из методов подкласса.

Источник

Приватные классы. Сокрытие в php

В php как и в большинстве других ООП языков существуют модификаторы видимости. Это ключевые слова public, protected и private. Но они применимы исключительно к свойствам, методам или константам. Данные модификаторы тесно связаны с возможностью инкапсуляции данных. Стоит заметить, что в таких языках как java, C#, go (https://golang.org/doc/go1.4#internalpackages), ruby (https://habr.com/post/419969/), crystal (https://crystal-lang.org/reference/syntax_and_semantics/visibility.html) есть возможность ограничивать область видимость пакетов (packages) или классов\типов. В php нет возможности ограничивать область видимости для классов — любой подключенный класс доступен из любого другого класса. Однако можно эмулировать данную возможность с применением нескольких трюков.

Для чего вообще может понадобиться сокрытие на уровне классов:

Отдельно можно выделить разбиение «больших» классов на мелкие объекты. Хорошей практикой считается ограничивать сложность (и количество строк) как отдельных методов так и классов. Количество строк тут идёт как один из маркеров, что метод класса или сам класс берёт на себя лишнию ответственность. При рефакторинге public метода мы выносим его части в private\protected методы. Но когда по тем или иным причинам класс разрастается и мы выделяем из него отдельную сущность, эти самые private\protected классы переносятся в отдельный класс, тем самым мы косвенно открываем доступ к методам, которые ранее были ограничены областью видимости одного класса.

Теперь собственно сами способы эмуляции сокрытия.

На уровне соглашения оформления кода

Используя PHPDoc комментарии можно отметить класс, трэйт или интерфейс как internal (http://docs.phpdoc.org/references/phpdoc/tags/internal.html). При этом некоторые IDE (например PhpStorm) могут понимать такие метки.

Использовать runtime информацию

Во время исполнения кода можно проверить откуда был вызван конструктор класса. Например через метод debug_backtrace (http://php.net/manual/ru/function.debug-backtrace.php) или использовать аналогичный функционал Xdebug для контроля кода в dev\test окружении. Пример оформленного решения есть тут (https://coderwall.com/p/ixvnga/how-emulates-private-class-concept-in-php).

Использовать анонимные классы

Относительно новый функционал в php — анонимные классы (http://php.net/manual/ru/language.oop5.anonymous.php). Описав анонимный класс внутри защищенного метода мы добиваемся его сокрытия. Что бы не городить портянку определения класса внутри функции, можно описать «приватный» класс в отдельном файле как абстрактный, и уже расширять его в определении анонимного класса. Хороший пример использования данного метода есть по этой ссылке (https://markbakeruk.net/2018/06/25/using-php-anonymous-classes-as-package-private-classes/).

Исходя из найденного материала видно, что функционал сокрытия классов в какой то мере востребован (и существует во многих языках), однако практика его использования очень ограничена, возможно отсутствием описанием примеров в различных «best practices», сборников шаблонов и подобных источниках. Что на мой взгляд является довольно странным, что есть акцентирование на сокрытие внутренних методов и свойств объектов, но почти никто не обращает внимание, что более крупные логические куски кода в виде служебных классов библиотек или доменной области остаются в глобальном пространстве видимости.

Источник

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

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