class Base{ /* */ } class Foo extends Base{ /* */ } class Bar extends Foo { /* */ } $obj = new Bar();
Но если вы решили, что это все таки необходимо — у меня есть для вас решение.
Чтобы достигнуть постановлений цели нам понадобится немного магии а именно магический метод __call, который будет вызван при попытке доступа к методу недоступному в контексте объекта, функцию call_user_func_array для вызова пользовательской функции с массивом параметров, а так-же, как вспомогательный инструмент, класс ReflectionClass, его мы будем использовать для получения некоторой информации о классе, ну и еще несколько стандартных функций.
Алгоритм решения прост, когда происходит обращение к недоступному методу объекта, срабатывает магический метод __call, в который передаются имя метода и параметры, нам на основе этой информации остается подключить файл с нужной функцией и вызвать её. Файл MyClass.php:
class MyClass{ public function __call( $method, $params = array() ) { // Получение имени класса $class_name = get_class( $this ); // Имя внешнего метода состоит из имени класса и имени метода $external_method_name = 'calss_' . $class_name . '_method_' . $method; // Проверяем, возможно функция ( внешний метод класса) уже есть, если нет, // пробуем подключить файл с функцией if( ! function_exists($external_method_name) ) { // Ожидается, что файл расположен рядом с файлом класса, местоположение // которого определим с помощью ReflectionClass $r = new ReflectionClass($class_name); $fn = dirname( $r->getFileName() ) . '/' . $external_method_name . '.php'; // Если файл существует, подключаем его if( file_exists( $fn ) ) { require_once( $fn ); } } // Проверяем, существует ли функция ( внешний метод класса) if( function_exists($external_method_name) ) { // В качестве первого параметра добавим текущий объект $this array_unshift( $params, $this ); // Вызываем функцию и возвращаем результат return call_user_func_array( $external_method_name, $params ); } else { // Функции не существует. Генерируем исключение trigger_error('Call to undefined method '.$class_name.'::'.$method.'()', E_USER_ERROR ); } }
Внешний метод класса, представляет собой функцию, в качестве первого параметра которой передается сам объект. Файл calss_MyClass_method_hello.php:
function calss_MyClass_method_hello( $that ) { echo( 'Hello World' ); }
Теперь можно проверить:
require( 'MyClass.php' ); $object= new MyClass(); $object->hello();
Данное решение рабочее, но имеет недостаток, мы не можем из внешнего метода, обращаться к свойствам и методам помеченным, как private. Эту проблему можно обойти расширив метод __call для приватных методов ( только для php >= 5.3 ) и добавив пару магических методов __get и __set для приватных свойств. Магические методы __get и __set вызываются при попытке доступа к свойствам недоступных в контексте объекта. Следует учитывать что приватные методы и свойства должны быть доступны только для внешних методов класса и не должны быть доступны из других мест, с этим нам поможет справиться функция debug_backtrace. Еще к недостаткам данного метода можно отнести и то, что внешние методы класса нельзя сделать приватными.
Далее я покажу, как расшарить приватные свойства для внешнего метода.
Добавим пару приватных свойств в класс:
private $_message = 'Hello'; private $_external_methods = array(); // Будем хранить имена функций, для которых разрешен доступ
Изменим немного метод __call, а именно секцию второй проверки существования функции:
// Проверяем, существует ли функция ( внешний метод класса) if( function_exists($external_method_name) ) { // Запомним имя функции, как собственный метод $this->_external_methods[ $external_method_name ] = TRUE; // В качестве первого параметра добавим текущий объект $this array_unshift( $params, $this ); // Вызываем функцию и возвращаем результат return call_user_func_array( $external_method_name, $params ); } else { // Функции не существует. Генерируем исключение trigger_error('Call to undefined method '.$class_name.'::'.$method.'()', E_USER_ERROR ); }
Получение значения приватного свойства:
public function __get( $var ) { // Получение имени класса $class_name = get_class( $this ); // Получим информацию о месте вызова метода __get $bt = next( debug_backtrace() ); // Проверим является ли функция внешним членом класса if( $var !== '_external_methods' && count($bt) && in_array( $bt['function'], $this->_external_methods )) { // Проверяем существует ли запрошенное свойство среди свойств класса if( array_key_exists( $var, get_class_vars( $class_name ) )){ // Ворачиваем результат return $this->{$var}; } else { // Совйство не найдено. Генерируем исключение trigger_error('Undefined property '.$class_name.'::$'.$var.''); } } else { // Доступ запрещен. Генерируем исключение trigger_error('Cannot access private property '.$class_name.'::$'.$var.'', E_USER_ERROR); } }
Запись свойства:
public function __set( $var, $val ) { // Получение имени класса $class_name = get_class( $this ); // Получим информацию о месте вызова метода __set $bt = next( debug_backtrace() ); // Проверим является ли функция внешним членом класса if( $var !== '_external_methods' && count($bt) && in_array( $bt['function'], $this->_external_methods )) { // Проверяем существует ли запрошенное свойство среди свойств класса if( array_key_exists( $var, get_class_vars( $class_name ) )){ // Изменяем значение свойства $this->{$var} = $val; } else { // Совйство не найдено. Генерируем исключение trigger_error('Undefined property '.$class_name.'::$'.$var.''); } } else { // Доступ запрещен. Генерируем исключение trigger_error('Cannot access private property '.$class_name.'::$'.$var.'', E_USER_ERROR); } }
Изменим код внешнего метода:
function calss_MyClass_method_hello( $that, $message = '' ) { // Меняем приватное свойство $that->_message = $message; // Получаем значение свойства и выводим на экран echo( $that->_message ); }
Проверяем:
require( 'MyClass.php' ); $object= new MyClass(); // Вызов внешнего метода $object->hello( 'Hello World!!!' ); // Попытка доступа к приватному свойству не из объекта // и не из внешнего метода класса вызовет исключение echo( $object->_message );
Цель достигнута. Доступ к приватным методам делается по принципу свойств, но в PHP версии ниже 5.3 этот трюк не сработает, магический метод __call не будет вызван, вместо этого сразу вызывается исключение.
ссылка на оригинал статьи http://habrahabr.ru/post/179437/
Добавить комментарий