Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > PHP: Общие вопросы > Хитрый синтаксис


Автор: BSCheshir 21.11.2012, 16:01
подскажите, есть ли методы, позволяющие реализовать такое поведение для произвольного числа параметров
Код

    function f1($name='instance',$params=Array()){
        list($p1,$p2,$p3,$p4)=$params;
        return f2($p1,$p2,$p3,$p4);
    }
function f2(){
...
}

где f2 может быть любой функцией (т.е. внутрь для решения задачи не лазим)

Автор: Sanchezzz 21.11.2012, 16:35
Не совсем ясно что вы хотите.. Вы хотите реализовать выполнение в F1 произвольную функцию через параметр? 
Это называется анонимные функции в 5.3+
Цитата

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

для создания функций с переменным количеством N Аргументов есть функция func_get_args() В 5.3 версии есть приятные изменения которых раньше не было


Автор: BSCheshir 21.11.2012, 17:13
нет, я хочу передать функции набор параметров, который предоставлен в массиве
Код

$params=Array($val1,'13hyzt',$val2,42)
list($p1,$p2,$p3,$p4)=$params;
f2($p1,$p2,$p3,$p4);


и спрашиваю: есть ли методы, позволяющие реализовать такое поведение для произвольного числа параметров?

чтобы понятнее - существует ли какой-нить такой синтаксис: 
Код

 f2(func_set_args($params));

или
Код

func_set_args('f2',$params);


Код

call_user_func('f2',$params);



Называется - задай правильно вопрос - получишь в нём ответ
Код

call_user_func_array('f2',$params);


вот. это, теоретически, оно  smile  smile 
Код

    function f1($name='instance',$params=Array()){
        return call_user_func_array('f2',$params);
    }
function f2(){
...
}


Так, а теперь осталось вспомнить, что надо не функцию вызвать, а создать экземпляр класса smile)) Как тут быть?
Код

    public static function factory($name='instance',$params=Array()){
        list($p1,$p2,$p3,$p4)=$params;
        return self::$_instance_list[$name] = new self($p1,$p2,$p3,$p4);
    }

Автор: Aliance 21.11.2012, 17:39
http://php.net/manual/ru/function.extract.php?

Автор: BSCheshir 21.11.2012, 17:48
Нет, не extract

Код

return self::$_instance_list[$name] = call_user_func_array(Array(get_called_class(),new self),$params);

Код

return self::$_instance_list[$name] = call_user_func_array(Array(get_called_class(),'__construct'),$params);


что-то наподобие такого должно по идее быть


о. или этого.
http://www.php.net/manual/ru/reflectionmethod.invokeargs.php


You can't call the constructor of $class like this:
Код

call_user_func_array (new $class, $args);


That's no http://ua2.php.net/callback as first parameter. Let's pick this apart:
Код

call_user_func_array (new $class, $args);

Is the same as
Код

$obj = new $class;
call_user_func_array ($obj, $args);

Автор: Fortop 21.11.2012, 23:52
Код

class C {
    public function setParams() {
    }
}


$c = new C
$c->setParams($args);

Автор: Sanchezzz 22.11.2012, 08:13
Мне такое чувство что вы пытаетесь создать второй Underscore.php

Автор: ksnk 22.11.2012, 09:33
http://ru2.php.net/manual/en/reflectionclass.newinstanceargs.php

Код

$rc = new ReflectionClass('SomeClass');
$class = $rc->newInstanceArgs(array('foo', 'bar'));
 
echo $class->doSomething();

откуда-то из недр php.net

Автор: BSCheshir 22.11.2012, 11:37
ksnk, вот он - метод моей мечты  smile

Есть некоторые ограничения, но... Оно  smile

в итоге получилось чуть сложнее
Код

...
    protected static $_instance_list = array(); 

    public static function factory($name='instance',$params=Array()){
        $called_class=get_called_class();
        $rc = new ReflectionClass($called_class);
        if ($rc->getConstructor())
            return $called_class::$_instance_list[$name] = $rc->newInstanceArgs($params);
        else
            return $called_class::$_instance_list[$name] = new $called_class;
    }
...


 Sanchezzz, ну разве что в этой части
Код


  // Singleton
  private static $_instance;
  public function getInstance() {
    if(!isset(self::$_instance)) {
      $c = __CLASS__;
      self::$_instance = new $c;
    }
    return self::$_instance;
  }


Автор: Fortop 22.11.2012, 19:56
BSCheshir, вы можете привести вменяемый пример использования этого конструкта?

Автор: BSCheshir 23.11.2012, 11:24
Fortop, ещё и вменяемый? Он же и так красивый, зачем вменяемый? Ну, щас попробуем изобразить

Код

abstract class Atom_class {
    protected static $_instance_list = array(); 
    public static function factory($name='instance',$params=Array()){
        $called_class=get_called_class();
        $rc = new ReflectionClass($called_class);
        if ($rc->getConstructor())
            return $called_class::$_instance_list[$name] = $rc->newInstanceArgs($params);
        else
            return $called_class::$_instance_list[$name] = new $called_class;
    }
}

Some_class extends Atom_class{
    protected $enebled=true;
    function doSomething(){
        if (isset($this->_instance_list)&&is_array($this->_instance_list)&&count($this->_instance_list))
            foreach ($this->_instance_list as $_key=>$_value)
                $_value->close();
    }
    Function Close(){
        $this->enebled=false;
    }
}

Another_class extends Atom_class {
    function __construct(){
        $params=funct_get_args();
        if (isset($params)&&is_array($params)&&count($params))
            foreach ($params as $_key=>$_value)
                $this->'p_'.$_key=$_value;
    }
    function save_all(){
        if (isset($this->_instance_list)&&is_array($this->_instance_list)&&count($this->_instance_list))
            foreach ($this->_instance_list as $_key=>$_value)
                $_value->save();    
    }
    function save(){}
}

Some_class::factory();
Some_class::factory('some_exemplar',Array('12',12312,$some_val))->doSomething();

Another_class::factory($i++,Array('hrr!','vrr!','mur!','varh!');
Another_class::factory($i++,Array('rr','aa','uu','!!');
Another_class::factory()->save_all();

Автор: Fortop 23.11.2012, 21:19
Цитата(BSCheshir @  23.11.2012,  11:24 Найти цитируемый пост)
 Он же и так красивый, зачем вменяемый? 

Ну я не увидел смысла в этой фабрике.
Красоты соответственно.



Автор: ksnk 24.11.2012, 10:19
Fortop, Какая разница? 
Вызвать конструктор объекта с определенным числом параметров, или функцию объекта, с тем-же числом параметров, предварительно вызвав его конструктор без параметров. 
Во втором случае нам нужен специфический класс, который позволит с собой так поступить, а в первом - любой. В каком месте более некрасиво?


Автор: Fortop 25.11.2012, 02:40
ksnk, ничерта не понял smile

Код

$someClass = new SomeClass(array('12',12312,$some_val));
$someClass->doSomething();

$anotherClass  = new AnoterClass(array('rr','aa','uu','!!'));
$anotherClass->doAnotherthing();


Цитата(ksnk @  24.11.2012,  10:19 Найти цитируемый пост)
Во втором случае нам нужен специфический класс, который позволит с собой так поступить, а в первом - любой. В каком месте более некрасиво?

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

Автор: ksnk 25.11.2012, 09:54
Присмотрелся, наконец, к коду BSCheshir, и тоже не понял смысла 

Fortop, Мои рассуждения касались другого случая. Решается задача создания объекта по имени и известному набору параметров. Если пользоваться ReflectionClass, ее можно решить для любых классов(первый случай). Если не пользоваться - только для специально под это заточенных( второй).


Автор: Fortop 25.11.2012, 23:01
Цитата(ksnk @  25.11.2012,  09:54 Найти цитируемый пост)
 Решается задача создания объекта по имени и известному набору параметров. Если пользоваться ReflectionClass, ее можно решить для любых классов(первый случай). Если не пользоваться - только для специально под это заточенных( второй).

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

Автор: BSCheshir 26.11.2012, 13:51
ksnk, Fortop, спасибо за критику, конечно, но 
1. 
Цитата(ksnk @  25.11.2012,  09:54 Найти цитируемый пост)
Решается задача создания объекта по имени и известному набору параметров.

2. В примере хотел показать создание коллекции экземпляров, для "пакетной" обработки, если что. 
Что конкретно не красиво? 

Автор: Fortop 27.11.2012, 00:09
Цитата(BSCheshir @  26.11.2012,  13:51 Найти цитируемый пост)
Что конкретно не красиво?

Отсутствие смысла.
Код ведь красив не сам по себе. 

Цитата(BSCheshir @  26.11.2012,  13:51 Найти цитируемый пост)
В примере хотел показать создание коллекции экземпляров, для "пакетной" обработки, если что. 

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

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

Автор: BSCheshir 27.11.2012, 11:16
т.е. с твоей точки зрения более красиво то же самое так записать?
Код

abstract class Base_class extends Atom_class implements ISaver {}

interface ISaver{
    public function constructor_emulator();
    public function save();
}

class Some_class extends Base_class {
    public function constructor_emulator(){
        {echo'<pre>Some_class __construct func_get_args() ';var_dump(func_get_args());echo'</pre>';}
    }
    public function __construct(){
        {echo'<pre>Some_class __construct func_get_args() ';var_dump(func_get_args());echo'</pre>';}
    }
    public function save(){
        {echo'<pre>Some_class save $this ';var_dump($this);echo'</pre>';}
    }
}

class Another_class extends Base_class{
    public function constructor_emulator(){}
    public function save(){
        {echo'<pre>Another_class save func_get_args() ';var_dump(func_get_args());echo'</pre>';}
        {echo'<pre>Another_class save $this ';var_dump($this);echo'</pre>';}
    }
     static $_instance_list = array(); 
}

abstract class Atom_class {
    protected static $_instance_list = array(); 
    public static function factory($name='instance',$params=Array()){
        $called_class=get_called_class();
        $rc = new ReflectionClass($called_class);
        if ($rc->getConstructor())
            return $called_class::$_instance_list[$name] = $rc->newInstanceArgs($params);
        else
            return $called_class::$_instance_list[$name] = new $called_class;
    }
}




Или каждый раз при создани каждого следующего экземпляра каждого класса (даже не фабрикой) нужно писать 
Код

$some_exemplar=new Some_class;
$some_exemplar->constructor_emulator();
$some_exemplar->set_params(Array($param1,$param2...))

Автор: Fortop 27.11.2012, 12:13
Я выше пример давал

Код

class C {
    public function setParams() {
    }
}
$c = new C
$c->setParams($args);


Но ты невнимательно прочел про параметры массивом
Код

class C {
    public function __construct(array $args) {
    }
}
$c = new C($args);


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

Код

class C {
    public function __construct($arg1 = 'default value', $arg2 = 'default value', $arg3 = 'default value') {
    }
}

$c[0] = new C(1,2);
$c[1] = new C(3);
$c[2] = new C(4,5,6);



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

Автор: BSCheshir 27.11.2012, 15:06
То что не возражаю по всем пунктам - может значить не только то, что невнимательно читаю, но и то, что согласен;) Но раз уж зашел разговор о параметрах в массиве. 

Все отлично, даже более - можно ассоциативным массивом передавать - 
Код

class D {
    public function __construct(array $args) {
        $args=@func_get_arg(0);
        if(isset($args)&&is_array($args)&&count($args))
            extract($args,EXTR_REFS);
...
    }
}
$d = new D($args);


Вопрос в том, что не подходит в применении к уже существующим классам, которые написаны без учёта того, что будет передаватся массив. Или, например, у одного передаётся ассоциативный массив первым параметром, у другого - список аргументов, у третьего - допустим, пусть будет массив из примера.
Цитата(Fortop @  27.11.2012,  12:13 Найти цитируемый пост)
class C {
    public function __construct(array $args) {
    }
}
$c = new C($args);


Автор: baldina 27.11.2012, 16:49
BSCheshir, остался основной вопрос: зачем всё это нужно?  smile 
вы изобрели вариант http://ru.wikipedia.org/wiki/%D0%92%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B2%D0%B8%D1%81%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8, однако полезность его не очевидна, а в проектах (фреймворках), где DI интенсивно эксплуатируется, есть свои решения, не самые плохие.

Автор: Fortop 27.11.2012, 22:16
Цитата(BSCheshir @  27.11.2012,  15:06 Найти цитируемый пост)
Вопрос в том, что не подходит в применении к уже существующим классам, которые написаны без учёта того, что будет передаватся массив.

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

Автор: ksnk 27.11.2012, 22:47
Fortop, Одно из применений - инициализация классов из файла конфигурации.

К примеру, класс mail, отсылающий сообщения, может быть стандартным mail'ом, SMTP транспортом "через mail-ru-google-com", skype и icq мессенджером и так далее. Инициализация каждого из них требует разных параметров, однако интерфейс всех классов сведен к фабричному mail'у. 
Параметры инициализации нужного транспорта - ассоциативный массив с параметрами в конфиге. С точки зрения юзера - нужно только выбрать нужный транспорт по названию. 

Вполне, на мой взгляд, кошерная схема.

Автор: Fortop 28.11.2012, 11:06
Цитата(ksnk @  27.11.2012,  22:47 Найти цитируемый пост)
Fortop, Одно из применений - инициализация классов из файла конфигурации.

Причем чужих классов транспорта
С разнородными сигнатурами.
И нужно это в недрах фреймворка где-то глубоко.
Цитата(Fortop @  27.11.2012,  22:16 Найти цитируемый пост)
Я не знаю сколько там у вас легаси кода, но на мой личный взгляд проще привести к общим интерфейсам, да и полезнее в будущем.



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

Автор: baldina 28.11.2012, 11:31
спор о dependency injection сродни религиозному  smile 

Автор: Fortop 28.11.2012, 12:19
Лично мне Di удобен в таком варианте

Код

$mail = new Mail (new Smtp($params));

$mail2 = new Mail(new Skype($params));


Так что спора особо не вижу.

P.S. как один из вариантов
http://phemto.sourceforge.net/quick-start.php

Автор: BSCheshir 28.11.2012, 13:54
Fortop, т.е. всё-таки удобнее передавать параметры инициализации в конструктор, да? Или тут от предмета дискуссии зависит? ;)

Автор: Fortop 28.11.2012, 14:13
Цитата(BSCheshir @  28.11.2012,  13:54 Найти цитируемый пост)
Fortop, т.е. всё-таки удобнее передавать параметры инициализации в конструктор, да? Или тут от предмета дискуссии зависит? ;) 


Вот так удобнее
Цитата(Fortop @  27.11.2012,  22:16 Найти цитируемый пост)
Я не знаю сколько там у вас легаси кода, но на мой личный взгляд проще привести к общим интерфейсам, да и полезнее в будущем.


Автор: xoptov 1.5.2013, 15:02
Цитата(BSCheshir @ 21.11.2012,  16:01)
подскажите, есть ли методы, позволяющие реализовать такое поведение для произвольного числа параметров
Код

    function f1($name='instance',$params=Array()){
        list($p1,$p2,$p3,$p4)=$params;
        return f2($p1,$p2,$p3,$p4);
    }
function f2(){
...
}

где f2 может быть любой функцией (т.е. внутрь для решения задачи не лазим)

Это не синтакис хитрый, это ты программист никакой...

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)