В рамках данной статьи, я хотел бы рассказать о том как можно сделать закрытую функцию открытой в PHP за счет хитрости с уровнями доступа функций класса при наследовании.
Один из неприятных моментов в использовании чужих библиотек и классов является то, что периодически можно столкнуться с тем, что некоторые методы закрытые (например, protected), из-за чего приходится в своих классах писать дополнительные обертки с корявыми названиями или попросту копировать код, чтобы сделать возможным их вызов вне класса (то есть аналог public). Например, такое очень полезно при тестировании, когда необходимо выдать отладочную информацию. Или еще пример - это просто полезные функции, такие как очистка строки от символов и прочее (функции, которые не зависят от контекста и могут пригодится и не только внутри класса).
Проблема тут так же заключается и в том, что частенько вы не можете менять исходные коды, так как при выходе следующей версии библиотеки все ваши "труды" улетят "коту под хвост".
Однако, с этой проблемой можно легко справиться из-за хитрости с уровнями доступа функций при наследовании класса. Чтобы понять, о чем я говорю, попробуем рассмотреть следующий код:
<?php
class A
{
//protected function someFunc($a)
// Вызовет Strict Standards: Declaration of B::someFunc() should be compatible with A::someFunc($a)
// Кстати, даже если определимть значение по умолчанию, то все равно будет ошибка
// Однако, вызвать через parent все равно можно
protected function someFunc()
{
echo 'Я защищенная функция someFunc класс A' . '<br/>';
} public function someFuncRev()
{
echo 'Я незащищенная функция someFuncRev класс A' . '<br/>';
}
public function testExecSomeFunc()
{
$this->someFunc();
}
}class B extends A
{
/*
// Двойное определение в классе нельзя совершать
protected function someFunc()
{
echo 'Оппа' . '<br/>';
}
*/
// А вот переопределять с повышением уровня доступа легко
public function someFunc()
{
//parent::someFunc();
echo 'А теперь незащищенная someFunc класс B' . '<br/>';
}
/*
// А вот это вызов ошибку
// Fatal error: Access level to B::someFuncRev() must be public (as in class A) i
protected function someFuncRev()
{
echo 'А теперь защищенная' . '<br/>';
}
*/
// При этом всегда остается одна и та же возможность вызвать метод
// отдельно через parent и отдельно через self
public function testParentAndSelf()
{
parent::someFunc();
self::someFunc();
}
}// Созданим экземпляр и посмотрим результаты
echo '-----------------------------' . '<br/>';
$a = new B;
echo 'Проверяем доступ к методу someFunc' . '<br/>';
$a->someFunc();
echo '-----------------------------' . '<br/>';
echo 'Проверяем доступ к методу someFuncRev' . '<br/>';
$a->someFuncRev();
echo '-----------------------------' . '<br/>';
echo 'Проверяем доступ через вызов parent и self' . '<br/>';
$a->testParentAndSelf();
echo '-----------------------------' . '<br/>';
echo 'Проверяем доступ через вызов someFunc из функции testExecSomeFunc класса A' . '<br/>';
$a->testExecSomeFunc();
Примечание: В коде представлены и описаны различные вариации с тем, к чему будет приводить тот или иной код, если его раскомментировать.
Открыв страничку с этим кодом мы получим следующий результат вывода:
-----------------------------
Проверяем доступ к методу someFunc
А теперь незащищенная someFunc класс B
-----------------------------
Проверяем доступ к методу someFuncRev
Я незащищенная функция someFuncRev класс A
-----------------------------
Проверяем доступ через вызов parent и self
Я защищенная функция someFunc класс A
А теперь незащищенная someFunc класс B
-----------------------------
Проверяем доступ через вызов someFunc из функции testExecSomeFunc класса A
А теперь незащищенная someFunc класс B
Как видите, в классе "A" есть функция "someFunc", но она помечена "protected". То есть если создать объект класса "A", то вызвать эту функции извне нельзя. Однако, если определить класс "B", который наследуется от класса "A", и в нем сделать функцию с точно таким же именем "someFunc" (и набором параметров), но пометить ее "public" (то есть открытой), то вызвать такую функцию становится возможным.
При этом важно понимать, что вы по-прежнему можете вызвать исходную функцию "someFunc" через конструкцию "parent", что демонстрируется в вызове функции "testParentAndSelf".
И еще один важный момент. Как вы понимаете, при переопределении функции в момент наследования, все функции класса "A", которые вызывали функцию "someFunc" через "$this" начнут вызывать функцию класса "B". Это видно при вызове функции "testExecSomeFunc". Так вот, чтобы этого избежать достаточно в классе "B" в функции "someFunc" только раскомментировать код "parent::someFunc();" и закомментировать "echo".
Если так сделать, то результат выполнения этого кода станет следующим:
-----------------------------
Проверяем доступ к методу someFunc
Я защищенная функция someFunc класс A
-----------------------------
Проверяем доступ к методу someFuncRev
Я незащищенная функция someFuncRev класс A
-----------------------------
Проверяем доступ через вызов parent и self
Я защищенная функция someFunc класс A
Я защищенная функция someFunc класс A
-----------------------------
Проверяем доступ через вызов someFunc из функции testExecSomeFunc класса A
Я защищенная функция someFunc класс A
Как видите, после такой вот хитрости, теперь и код самой функции остается не тронутым, и никаких костелей не требуется, и функцию можно без проблем использовать вне класса. То есть если вам нужны какие закрытые функции сделать открытыми, то достаточно написать класс, где будут перечислены эти функции и будет использоваться вызов закрытых функций через "parent".
Что-то вроде:
class ImportantClass
{
//,,,,
// Некая важная и нужная закрытая функция
protected function NeedProtectedFunction()
{
//....
}
}
// $a = new ImportantClass;
// $a->NeedProtectedFunction(); // Ошибка
// Маленькая обертка
class MyImportantClass extends ImportantClass
{
public function NeedProtectedFunction() { parent::NeedProtectedFunction(); }
}
$b = new MyImportantClass;
$b->NeedProtectedFunction(); // Выполнится без ошибок и не вызовет проблем внутри кода исходного класса
Если вы знаете еще какие-то хитрости, то смело делитесь ими в комментариях
Комментарии / отзывы