[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: передать конструктор в call_user_func_array()
bvn
Давно я не писал здесь вопросов, а вот таки теперь серьезно застрял.
Задача, пишется лоадер файлов, допустим, классов.... классов в том числе. Метод, загружающий класс должен уметь инстанцировать его при необходимости, а если это класс сторонней библиотеки, то по мере надобности передать в конструктор параметры. Выглядеть это должно приблизительно так:

$obj = $load->lib('libname', true, $args), где true - это "да, инстанцировать", ну и $args - массив параметров.

Как такое реализовать? Первое, что в голову пришло, это наивное new LibName(extract($args)) smile.gif
Естественно такое не прокатывает, но потом я вспомнил про функцию call_user_func_array(), но... как собственно вызвать конструктор? Указывать 'LibName::__construct' или array('LibName', '__construct') - не прокатывает, как нельзя и объявить конструктор статическим, к тому же мы договорились стороннюю либу не модифицировать...

Ничего не остается, как
$load->lib('libname', false);
$obj = new LibName($arg1, $arg2...$argN);

Или может, кто-нибудь знает способ решить проблему?



Спустя 9 часов, 36 минут, 50 секунд (11.04.2010 - 06:46) kirik написал(а):
Вот вариант:
class a {
function __construct() {
echo 'a::__construct()';
var_dump(func_get_args());
}
}


class b {
function load($libname) {
$obj = new $libname();
call_user_func_array(array($obj, '__construct'), array('param1'));
}
}


$loader = new b();

$loader->load('a');


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

Спустя 6 часов, 7 минут, 8 секунд (11.04.2010 - 12:53) glock18 написал(а):
думаю, более верным будет другое решение (это требует, чтобы все аргументы были опциональными и конструктор вдобавок будет вызываться дважды):

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

class a extends libClass {
public function __construct()
{
$args = func_get_args();
if (!empty($args)) {
call_user_func_array(array('parent', '__construct'), $args);
}
}
}


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

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

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

Спустя 7 часов, 3 минуты, 3 секунды (11.04.2010 - 19:56) kirik написал(а):
Цитата (glock18 @ 11.04.2010 - 04:53)
конструктор родителя может иметь обязательные параметры, а на некоторых версиях пхп конструктор потомка должен иметь такие же аргументы, иначе вызовет ошибку.

Я обычно частоиспользуемые библиотеки подгружаю через обертку-синглтон типа:

class a {
public function __construct() {
echo 'a::__construct()';
var_dump(func_get_args());
}

public function fn1() {
echo 'a::fn1()';
}
}


class aWrapper {
static $instance;
static public function gI() {
if(!self::$instance) {
self::$instance = new a('param1', 'param2');
}
return self::$instance;
}
}


aWrapper::gi()->fn1();


Например для класса работы с БД или для шаблонизатора этот подход себя оправдал. Из любого места программы мы имеем доступ к функциям.

Спустя 1 день, 20 часов, 40 минут, 33 секунды (13.04.2010 - 16:37) bvn написал(а):
За советы всем спасибо. Враппер писать - это всегда можно, но это не универсальное решение. В идеале хочется, чтобы либу вкинул во фреймворк и можешь лоадить, сколько захочется... Да и лишнее наследование ради только самой загрузки... Вобщем, нашел кое-какое решение без враппера... Сейчас меня будете ругать наверное wink.gif


if (isset($args)) {
if (is_array($args)) {
// Если массив
$code = '$obj = new '.$name.'(';
foreach ($args as $arg)
$code .= '"'.$arg.'",';
$code = rtrim($code, ',').');';
eval($code);
} else {
// Если один параметр
$obj = new $name($args);
}
} else {
// Если без параметров
$obj = new $name;
}
return $obj;


Как вам такой вариант?

Спустя 2 часа, 51 минута, 41 секунда (13.04.2010 - 19:29) kirik написал(а):
Цитата (bvn @ 13.04.2010 - 08:37)
Как вам такой вариант?

Ужасен.

Цитата (bvn @ 13.04.2010 - 08:37)
В идеале хочется, чтобы либу вкинул во фреймворк и можешь лоадить, сколько захочется...

Хоти wink.gif

Спустя 38 минут, 15 секунд (13.04.2010 - 20:07) twin написал(а):
Извращение...
Причем с начала идеи и до реализации.

Спустя 35 минут, 5 секунд (13.04.2010 - 20:42) bvn написал(а):
Гммм... У критики еще бывает аргументация, хотелось бы ее в первую очередь услышать. Благодарю.
Только в исходный вариант, я вношу небольшую коррективу, чтобы корректнее работать с данными любых типов, включая массивы:


if (isset($args)) {
if (is_array($args)) {
// Если массив
$code = '$obj = new '.$name.'(';
foreach ($args as $key => $arg)
$code .= '$args["'.$key.'"],';
$code = rtrim($code, ',').');';
eval($code);
} else {
// Если один параметр
$obj = new $name($args);
}
}
else {
// Если без параметров
$obj = new $name;
}
return $obj;

Спустя 39 минут, 10 секунд (13.04.2010 - 21:21) twin написал(а):
Аргументации? Да пожалуйста.
Очень мне нравится высказывание Стерлинга Хьюза:
Цитата
У меня такое чувство, что всё ООП состоит из превращения уже имеющихся задач в новые...
...Такие модели, особенно применительно к Web, получаются намного более сложными сами по себе, чем проблемы, для решения которых они создавались.

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

Есть же цивилизованные решения... Ведь универсальность обратно пропорциональна эфективности. Где то должна мера быть...

Спустя 21 минута, 48 секунд (13.04.2010 - 21:43) bvn написал(а):
Понятно, спасибо. Аргументация ясна. И на самом деле, все не так уж извратно, как было сразу сказано без аргументации. Коля, я понимаю, что у нас взгляды на программирование могут очень сильно отличаться, так как они отличались изначально... хотя в последующих твоих кодах (как-то скачал на твоем сайте CMS) я встречал идеи, изначально использованные в моем блоге, который мы попытались с тобой писать... помнишь "Наш Питер"?
Так к чему это я. В изначальной идее загрузчика, я ни чуть не больший извращенец, чем разработчики CodeIgniter. Надеюсь ты не будешь утверждать, что создатели одного из наиболее популярных фреймворков извращенцы? Единственное, чем я усложнил идею, добавил возможность передавать параметры конструктору. И "ужом извиваюсь" я, как ты говоришь, ради одного лишь - удобства использования. Для меня это основной критерий хорошего кода, лишь бы не в ущерб быстродействию. Но я почему-то думаю, что от этого одного несчастного eval быстродействие данного кода сильно не упадет... тем более, что другого-то решения исходной задачи, без модификации библиотеки, без всяких врапперов похоже-то и не существует...
Ну а что касается реализации, с удовольствием бы взглянул на твой вариант. Возможно, что мой код в этом плане не идеал, хочется писать красиво. Может покажешь более красивый вариант?
Благодарю за внимание.

Спустя 39 минут, 26 секунд (13.04.2010 - 22:22) twin написал(а):
Цитата
Может покажешь более красивый вариант?

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

А ад ждет тех, кто будет потом это все использовать. Ведь удобство - вещь весьма эфимерная и сугубо субъективная.
У меня дома есть кортофелечистка. Подарили как то. Очень удобная штука с одной стороны. Но.
1. Отходов получается в несколько раз больше, а значит кушать остается гораздо меньше.
2. Если вдруг картошка не совсем правильной формы или с изъяном - то она бессильна. Все равно приходится брать в руки ножик.

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

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

Очень часто всплывают подобного рода вопросы, потому что люди, изучив синтаксис CodeIgniter или Cake или даже ZEND, впадают в ступор и прострацию при возникновении малейшей нестандартной ситуации.

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

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

Я сторонник свободного программирования, не втиснутого в рамки фреймворков или других условностей

А по сему и позволил себе подобного рода высказывание:
Цитата
Извращение...
Причем с начала идеи и до реализации.


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

Но это разумеется ИМХО, весьма вероятно у тебя все же выйдет что то действительно революционное.

Спустя 8 часов, 56 минут, 57 секунд (14.04.2010 - 07:19) bvn написал(а):
Цитата (twin @ 13.04.2010 - 22:22)
Цитата
Надеюсь ты не будешь утверждать, что создатели одного из наиболее популярных фреймворков извращенцы?
Буду. Не столь категорично, но что то рядом. Я понимаю, что и у них и у тебя благие намерения. Но, как известно, ими вымощена дорога в ад.

После этих слов, понимаю, что мне твое мнение на счет моего кода совершенно не интересно, так как не авторитет ты мне после этого smile.gif
На счет остальных длинных тирад даже спорить не хочу. Имеешь право на собственное мнение. Единственное просьба в дальнейшем аккуратнее высказывать свои мнения, особенно негативные. Наверное, ты достаточно умный человек, чтобы предположить, что другие мнения тоже имеют право на жизнь. Благодарю за внимание.

Спустя 1 час, 13 минут, 10 секунд (14.04.2010 - 08:32) twin написал(а):
Всегда рад.

Другие мнения естественно имеют право не только жить, но и развиваться. Я просто пояснил свою точку зрения, обозначив её ИМХО для устранения каких либо неоднозначностей.

То, что я не авторитет для тебя, это предсказуемо. Как впрочем и для многих, кто считает фреймворки вершиной мироздания. И слова мои:
Цитата
Буду. Не столь категорично, но что то рядом. Я понимаю, что и у них и у тебя благие намерения. Но, как известно, ими вымощена дорога в ад.

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

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

Да они такие же точно программисты как ты и я, имеют свои убеждения и так же склонны ошибаться, как любой человек. По этому они конечно талантливые люди, нет спору. Но занимаются тем, что противоречит моим (а я далеко не единственный в этом лагере) взглядам на программирование.
И ты тоже, кстати.

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

И мне глубоко не интересно ваше обо мне мнение. smile.gif
И разумеется я не стану принимать в этом участия. Хотя иногда интересно наблюдать со стороны, как в очередной раз самозабвенно предпринимается попытка изобрести вечный двигатель.

Спустя 5 часов, 12 минут, 32 секунды (14.04.2010 - 13:45) glock18 написал(а):
bvn
eval нехорошо, это правда. желание обойтись без враппера понятно в общем-то. тогда могу предложить, что нужно править не классы, а лоадер. может что-то и выгорит из этого. опять же это если говорить об отсутствующем оверхеде - eval при большом количестве вызовом даст его в избытке.

Спустя 1 час, 40 минут, 27 секунд (14.04.2010 - 15:25) bvn написал(а):
Цитата (glock18 @ 14.04.2010 - 13:45)
bvn
eval нехорошо, это правда. желание обойтись без враппера понятно в общем-то. тогда могу предложить, что нужно править не классы, а лоадер. может что-то и выгорит из этого. опять же это если говорить об отсутствующем оверхеде - eval при большом количестве вызовом даст его в избытке.

На счет перегрузки, то на практике не очень часто приходится грузить классы с параметрами в конструкторе, поэтому большого количества вызовов скорее всего не будет.
Хотя, конечно, что eval - плохо, я согласен, но альтернативы я пока что не вижу. На счет править лоадер - как? Собственно весь вопрос именно в этом и стоит, как написать этот самый лоадер, чтобы обойтись без eval?
Решение от kirik не рабочее из-за неизвестности числа обязательных параметров конструктора, да и лишний его "холостой" вызов - как-то не красивше смотрится, чем мой eval, ИМХО.
Вот, если кто придумает изящный способ, то честь ему и хвала! smile.gif

Спустя 37 минут, 17 секунд (14.04.2010 - 16:03) bvn написал(а):
PS: Выкопал решение, которое выглядит красивше и без eval, да и по объему кода короче... Единственное на счет профайлинга, на одном вызове заметной разницы нет... надо будет создать какой-нибудь цикл для теста и проверить, что же быстрее... Но о тестах позже отпишусь, а сейчас решение:

$class = ReflectionClass($name);
if ($class->isInstatiable())
$obj = $class->newInstanceArg($args);

Прям, то шо дохтор прописал smile.gif

Спустя 9 минут, 55 секунд (14.04.2010 - 16:13) bvn написал(а):
Вобщем так, провел 10 000 загрузок одним и другим методом получил следующие результаты.

С помощью eval:

Start memory: 108308
Time: 21.104911 Memory: 6927184

С помощью ReflectionClass:

Start memory: 108308
Time: 12.006279 Memory: 5644608


Так, что мне честь и хвала biggrin.gif

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

Спустя 2 часа, 36 минут, 57 секунд (14.04.2010 - 18:50) twin написал(а):
Искренне рад за тебя. Действительно симпотично.
Прими поздравления. user posted image

Спустя 23 минуты, 41 секунда (14.04.2010 - 19:13) kirik написал(а):
Чет я не совсем подрубил как эта штука работает.. Что за объект возвращает ReflectionClass?

Спустя 3 часа, 34 минуты, 16 секунд (14.04.2010 - 22:47) bvn написал(а):
Тут можно почитать http://www.php.net/manual/en/book.reflection.php
Типа фича для реверсного инжиниринга классов... да и вообще всего, что есть, насколько я понял... Можно создать определенный класс-отражение любой функции или класса и узнать про него все: свойства, методы, параметры... инстанцировать и т. д.
Я глубоко не вчитывался, как освоил свою задачу, на этом уровне и остановился... поэтому в деталях могу ошибаться.

Спустя 3 минуты, 7 секунд (14.04.2010 - 22:51) bvn написал(а):
Цитата (twin @ 14.04.2010 - 18:50)
Искренне рад за тебя. Действительно симпотично.
Прими поздравления. user posted image

Спасибо за поздравления.

Спустя 1 час, 17 минут, 33 секунды (15.04.2010 - 00:08) kirik написал(а):
Цитата (bvn @ 14.04.2010 - 14:47)
Тут можно почитать http://www.php.net/manual/en/book.reflection.php

Спасибо!

Спустя 13 дней, 12 часов, 50 минут, 52 секунды (28.04.2010 - 12:59) screjet написал(а):
Проще пареной репы
function call_user_construct($class,$args=array())
{
$p = array();
foreach($args as $i=>$a){
${"a$i"} = $a;
$p[] = '$a'.$i;
}
return eval("return new $class(".join(",",$p).");");
}

Спустя 14 минут, 57 секунд (28.04.2010 - 13:14) twin написал(а):
Почитал бы топик сначала)))

Спустя 5 минут, 36 секунд (28.04.2010 - 13:20) screjet написал(а):
Цитата (twin @ 28.04.2010 - 10:14)
Почитал бы топик сначала)))

Та пейджер страниц не заметил, сделали совсем не заметным))

Спустя 8 месяцев, 19 дней, 12 часов, 32 минуты, 55 секунд (18.01.2011 - 02:52) Vasi написал(а):
Блуждая по бесполезному Интернету, набрёл на вас, ребята - и вы похоже тоже говорите по-русски (и страдаете в какой-то мере той же ... лабудой)
Тема уже, конечно, наверно устарела (да и после скрола вниз увидел, что все уже написали за меня),
Я столкнулся с той же проблемой, что и ты, bvn - надо было создавать экземпляры классов с публичным конструктором с хз какими параметрами на входе.
Самое "близкое" и "изящное" до чего дошел я (и описанное вами):

class someWeirdClass{
function __construct($a, $b, $c){}
}

$class = 'someWeirdClass';
$r = new ReflectionClass($class);
$inst = $r->newInstanceArgs(array('a','b',new stdClass());

НО:
- функция доступна PHP >= 5.1.3
Что там с тестом/бенчмарком? О-)

Спустя 7 часов, 2 минуты, 56 секунд (18.01.2011 - 09:55) linker написал(а):
Запомните, метод __construct() не является статическим методом. Данный метод всегда автоматически вызывается при $o = new Class(); и нет нужды делать это повторно $o->__construct();. Вообще не понятен смысл вопроса.

Спустя 3 часа, 22 минуты, 14 секунд (18.01.2011 - 13:18) Vasi написал(а):
Да, именно поэтому:

class Foo{}
$inst = call_user_func_array(array('Foo', '__construct'), $args);

Не отработает (т.к. Foo::__construct не является статичным методом).

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

class Wall{
public function __construct(Wall_Brick $brick, Wall_Cement $cement, IWorker $worker){}
}



И были предложены 2 альтернативы:
1. eval (кул-хацкер)
2. ReflectionClass

Судя по всему, выиграл ReflectionClass, т.к.:
- быстрее
- удобнее
- 2 строчки кода
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.