Модераторы: skyboy, MoLeX, Aliance, ksnk
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Велосипед++ 5.3, Метод расширения статического объекта. 
:(
    Опции темы
ksnk
Дата 21.9.2012, 22:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 1
Всего: 386



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

По аналогии с YII был обозначен глобальный класс-представитель, видимый в открытом космосе. Воизбежании некрасиво-длинного с моей точки зрения вызова YII::app()->handle() предлагается монтировать статические методы прямо в класс примерно так:

Код

<?php
/**
 * статический класс - преставитель CMS в космосе.
 */

class ENGINE
{

    private static $extensions = array();

    /**
     * Регистрация нового интерфейса
     *
     * @static
     * @param $method - имя метода
     * @param $callable - параметры регистрации
     */
    public static function register_interface($method, $callable)
    {
        if (is_callable($callable)) {
            self::$extensions[$method] = $callable;
        } else {
            self::error(sprintf('ENGNE:Wrong parameter awhile registerring interface `$s`', $method));
        }
    }

    /**
     * Сбросить регистрацию интерфейса
     *
     * @static
     * @param $method - имя метода
     */
    public static function unregister_interface($method)
    {
        unset(self::$extensions[$method]);
    }

    /**
     * функция вывода сообщения об ошибке. Не желательно пользоваться стандартным интерфейсным
     * механизмом, так как функция обязана работать в любом случае.
     *
     * @param $msg
     * @param null $args
     * @return mixed
     */
    public function error($msg, $args = null)
    {
        if (array_key_exists('error', self::$extensions)) {
            return call_user_func(self::$extensions['error'], $msg, $args);
        } else {
            echo $msg . " <br>\n";
        }
    }

    /**
     * Just a little magic
     * Простой механизм для монтажа расширений
     *
     * @param $method
     * @param $args
     * @return mixed
     */

    static public function __callStatic($method, $args)
    {
        if (array_key_exists($method, self::$extensions)) {
            return call_user_func_array(self::$extensions[$method], $args);
        } else {
            self::error(sprintf('ENGNE:Interface `$s` not defined', $method));
        }
    }

}
 

Чтобы добавить в класс некий новый метод, достаточно написать 
Код


    function sample_handler($msg,$arg=null){
       echo 'handler happen '.$msg;
    }

    ENGINE::register_interface('handle','sample_handler');

// с этого момента можно пользоваться статической функцией handle.
    ENGINE::handle('67890');



Возможно кто-нибудь уже сочинять подобную "во все стороны расширяемую" штуку. Какие есть/остались впечатления от пользования перекрытия статических методов с помощью __callStatic? Может какие серьезные потери в производительности есть?

Есть ли аналог для перехвата неизвестных статических методов для версии 5.2? Может там работает __call?


--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
Fortop
Дата 22.9.2012, 13:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2200
Регистрация: 13.11.2007
Где: Донецк

Репутация: 1
Всего: 42



Цитата(ksnk @  21.9.2012,  22:53 Найти цитируемый пост)
Есть ли аналог для перехвата неизвестных статических методов для версии 5.2? Может там работает __call? 

А проверить? smile


--------------------
Мир это Я.
Живее всех живых.
PM MAIL   Вверх
Sanchezzz
Дата 22.9.2012, 13:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1670
Регистрация: 19.11.2006
Где: Voronezh

Репутация: 2
Всего: 60



в 5.2 есть __call вроде работает использую в собственном фреймворке в контроллерах как 404 ошибку если метод через __call не нашелся полностью, правда у самого стоит 5.3.

Меня тоже в уааяй раздражает писать длинные цепочки







--------------------
Понравился ответ "+" по репе, не забываем закрывать тему, заказы в LS.
PM MAIL Skype GTalk   Вверх
ksnk
Дата 22.9.2012, 13:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 1
Всего: 386



Цитата(Fortop @  22.9.2012,  13:00 Найти цитируемый пост)
А проверить?

Не, не работает для статических классов, только так, как в YII. Это немного обидно, так как не на всех моих площадках пока стоит 5.3  :( 
Цитата(Sanchezzz @  22.9.2012,  13:34 Найти цитируемый пост)
в 5.2 есть __call вроде работает

Работает, но не для  статического вызова. Так что придется писать длинно и некрасиво  smile 




--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
ksnk
Дата 22.9.2012, 14:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 1
Всего: 386



В итоге - принято волюнтаристкое решение технично наплевать на 5.2 и писать в терминах 5.3.
Для деплоя на сервера с 5.2 будет производится текстовая замена 
Код

class ENGINE{

// if 5.2
   static $I;

   function __call($method,$args){
      ...
   }

// if 5.3
    function __callStatic($method,$args){
      ...
   }

}

//if 5.2
ENGINE::$I=new ENGINE();


а в остальных файлах все ENGINE:: будут заменяться на ENGINE::$I->

осталось только не запутаться по дороге и не написать что-то совсем уж несовместимое ...



--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
ksnk
Дата 22.9.2012, 15:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 1
Всего: 386



Небольшое пояснение по сути принятого выше решения.

Простенький benchmark предложенного в первом посте кода, после небольшого допиливания, на 4 варианта вызова 
  •  вызов статической функции (использовался встроенный error)
  •  вызов "статической" с помощью __callStatic ENGINE::...
  •  вызов "динамический" с помощью __call и ENGINE::$I->... где $I - экземпляр класса ENGINE.
  •  вызов "динамический" с помощью __call и ENGINE::II()->... где II возвращает $I из предыдущего примера.
показал довольно занимательные результаты. На 10000 вызовов получилось
Цитата

so spent 0.050411 sec for static, 0.096620 sec for ::,0.095476 sec for ::$I->,0.126145 sec for ::II()->

Если расшифровать, то первый вариант - это просто накладные расходы на вызов самой функции
Дополнительные расходы на __call и __callStatic составляют 0,05 на 10к вызовов и примерно равны между собой. Этот факт меня, собственно и поразил. Мне казалось, что __call в этом варианте должен проиграть __callStatic.
Дополнительные расходы на __call и выдачу инстанса составляют 0,07 на 10к. 

Понятно, что это все "спички", но на чем еще экономить в "ядрёном" коде?

Это сообщение отредактировал(а) ksnk - 22.9.2012, 15:48


--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
Sanchezzz
Дата 22.9.2012, 18:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1670
Регистрация: 19.11.2006
Где: Voronezh

Репутация: 2
Всего: 60



странно что __call  в старых версия будет так работать

тест был сделан без анонимных функций ?  function(){ ... }
Цитата

Понятно, что это все "спички", но на чем еще экономить в "ядрёном" коде?

тут урезать то нечего все просто)

Это сообщение отредактировал(а) Sanchezzz - 22.9.2012, 18:06


--------------------
Понравился ответ "+" по репе, не забываем закрывать тему, заказы в LS.
PM MAIL Skype GTalk   Вверх
ksnk
Дата 22.9.2012, 18:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 1
Всего: 386



Цитата(Sanchezzz @  22.9.2012,  18:05 Найти цитируемый пост)
тест был сделан без анонимных функций ?  function(){ ... }

Да.  Устанавливался в качестве стороннего обработчика тот же самый ENGINE::error.

Все многообразие новых возможностей 5.3 мне пока нельзя применять :( Пока остается несколько хостов на 5.2. Хорошо, хоть с 4-ки все уже слезли.  


--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
ksnk
Дата 6.10.2012, 11:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 1
Всего: 386



потихоньку оформляется. Дописан сервис хранения параметров - set_option - установить и option - получить параметр по имени

Обнаружил еще некоторую пользу.

Естественно и просто выходит ленивая загрузка. Для этого достаточно устанавливать обработчиками событий и интерфейсов статические методы классов.

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

Код

ENGINE::set_option('engine.interface',array(
  'log'=>'ENGINE_logger::log',
  'db'=>'ENGINE_database::db'
...
))
...
foreach (ENGINE::option('engine.interface',array()) as $k=>$v)
    ENGINE::register_interface($k,$v);
...


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

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

Код

class ENGINE_logger
{

    var $file = null;

    static $db_table = '';

    function file_logger($msg, $arg)
    {
        $arg['{{date}}'] = date(DATE_RFC822);
        fwrite($this->file, ENGINE::_t('{{date}} ' . $msg, $arg) . "\n");
    }

    /**
     * забота о закрытии файла лога
     */
    function __destruct()
    {
        if (!empty($this->file)) {
            fclose($this->file);
            $this->file = null;
        }
    }

    static function db_logger($msg, $arg)
    {
        if (!isset($arg['type'])) $arg['type'] = 'userlog';
        ENGINE::db()->query(
            'insert into `' . self::$db_table . '` set msg=?,type=?;'
            , array(ENGINE::_t($msg, $arg), $arg['type']));
    }

  /**
   *   __construct
   */

    static function log($msg, $arg)
    {
        $uri = parse_url(ENGINE::option('engine.logger_uri', 'file://logfile.log')); // for database - 'db:///logtable

        switch ($uri['scheme']) {
            case 'file':
                if (!empty($uri['host'])) $uri['path'] = $uri['host'] . $uri['path']; // so correct parse troubles
                // тут мы будем инициировать объект.
                $logger = new ENGINE_logger();
                $logger->file = fopen($uri['path'], 'a+');
                ENGINE::register_interface('log', array($logger, 'file_logger'));
                break;

            case 'db': // so let's use current database for logging
                self::$db_table = ENGINE::option('engine.log_table');
                ENGINE::db()->query('create table if not exists `' . self::$db_table . '`(
`id` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
`msg` VARCHAR( 200 ) NOT NULL ,
`type` VARCHAR( 8 ) NOT NULL ,
INDEX (  `date` )
) ENGINE = MYISAM ;');
                ENGINE::register_interface('log', 'ENGINE_logger::db_logger');
                break;
            //check if
             deault:
                ENGINE::error('Oops! Logger definition unrecognised.')
        }

        ENGINE::log($msg, $arg);
    }

}



Основной смысл - "начальная функция" интерфейса - сама и является конструктором объекта. Она обязана проанализировать ситуацию и установить правильный интерфейсный обработчик.

Случай с файловым логером переусложнен, можно было пользоваться error_log'ом и обойтись статической функцией, но тогда было бы не так показательно ...

Вообще, с возможностью переустановки "функций" в "объекте" на лету  - чисто Javascript'овский прием.


--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
Fortop
Дата 6.10.2012, 18:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2200
Регистрация: 13.11.2007
Где: Донецк

Репутация: 1
Всего: 42



Цитата(ksnk @  6.10.2012,  11:43 Найти цитируемый пост)
Вообще, с возможностью переустановки "функций" в "объекте" на лету  - чисто Javascript'овский прием. 

А можно более реальный пример когда это "кровь из носу" нужно?

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


--------------------
Мир это Я.
Живее всех живых.
PM MAIL   Вверх
ksnk
Дата 6.10.2012, 19:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 1
Всего: 386



Цитата(Fortop @  6.10.2012,  18:52 Найти цитируемый пост)
А можно более реальный пример когда это "кровь из носу" нужно?

Ну вот же, я выше пытался продемонстрировать.

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

В моем случае, вызов первый вызов ENGINE::log('some message') приведет к подгрузке файла с логером и вызову ENGINE_logger::log(...). Эта функция в процессе выполнения анализирует параметры конфигурации системы и выясняет - какую конкретно функцию система предпочитает использовать, вывод в файл или вывод в базу. После чего, обработчиком соответствующего интерфейса ENGINE::log становится конкретная функция и окружение  инициализируется (контроль таблицы для базы или открытие файла для файла).
Второе и следующие обращения уже вызовет конкретную функцию, у которой корректно проинициализировано окружение.


--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
Fortop
Дата 7.10.2012, 16:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2200
Регистрация: 13.11.2007
Где: Донецк

Репутация: 1
Всего: 42



ksnk, мысль я вобщем-то понял.
Я не понял какие удобства это дает в сравнении с существующими вариантами.


--------------------
Мир это Я.
Живее всех живых.
PM MAIL   Вверх
ksnk
Дата 7.10.2012, 16:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 1
Всего: 386



Цитата(Fortop @  7.10.2012,  16:19 Найти цитируемый пост)
Я не понял какие удобства это дает в сравнении с существующими вариантами.

Я пока тоже не понял smile Просто подозреваю, что довольно скоро мне понадобится компактный механизм для "внедрения" в существующий уже сайт. К сожалению, сам сайт написан так, что внедрится в него естественным образом, без серьезного рефакторинга, малореально. Придется мастерить "раковую опухоль" и приклеивать сбоку. 
В добавок, получается довольно удобное ядро для маленьких php-утилит. Надо для тестирования системы переписать что-нибудь из своих велосипедов...
 



--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
  
Ответ в темуСоздание новой темы Создание опроса

Внимание: данный раздел предназначен для решения сложных, нестандартных задач.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | PHP: Для профи | Следующая тема »


 




[ Время генерации скрипта: 0.1046 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.