#php
Приветствую! Знатоки выручайте, долгое время уже пытаюсь разобраться с этим вопросом и никак не могу найти информацию, а точнее нашел, но не то. Вообщем сама задача. Есть массив, который содержит в себе lambda функции: $functs = ['name' => function() { return 1; }]; Мне нужно создать для него редактирование, казалось бы все так просто, взять циклом пробежаться и всунуть все в input и textarea поля. Но возникла проблема сами функции они не выводятся так как нужно. А нужно чтобы они также выводились как определены в массиве, то есть строкой. function() { return 1; } Прям точно также - как и определены в значении массива. Пробовал делать так: $func= new ReflectionFunction($functs['name']); var_dump($func->__toString()); Но! Выводится мусор, не то, что нужно: Closure [function {closure} ] { @@ .....\index.php 11 - 13 } Уже какой день бьюсь в этом направлении и никак не могу решить задачу, чтобы было ключ массива в input, а значение в textarea. То есть вот так: Пожалуйста помогите или пните в правильном направлении. UPD: Массив может быть любого вида: Хоть такой: $functs = ['name' => function() {return 1;},'m'=>fucntion() {}]; В одну строку. Пробовал решить так: function closure(Closure $closure) { $function = new ReflectionFunction($closure); $file = file($function->getFileName()); for ($i = $function->getStartLine() - 1; $i < $function->getEndLine(); $i++) { $str .= $file[$i]; } $funct = mb_substr($str, mb_stripos($str, 'function', 0, 'utf-8'), -1, 'utf-8'); $funct = mb_substr($funct, 0, mb_strripos($funct, '}', 0, 'utf-8')+1, 'utf-8'); return $funct; } Работает, но только если размер массива 1. То есть такой: $functs = ['name' => function() {return 1;}]; а в случае: (new MyClosure($closure))->__toString(); возникает LogicException, что не совсем гуд, исключено, чтобы это было. нужно решение, чтобы в любом написании массива с lambda функциями. Кстате, ссылка с приведенным алгоритмом, не пашет тоже. Незнаю, как поправить свою функцию, так, чтобы отследить правильную позицию завершения блока функции " } ", а не искать конец " } ", а то получается, что он захватывает все и получается хрень. Похоже и длину строки надо считать и т.д., формула нужна, как верно отследить. Выручайте, не врублюсь, какое условие написать?
Ответы
Ответ 1
Вот примерчик: class ClosureDump { const SPLITTERS = PHP_EOL . ', '; protected $closure; protected $begin; protected $end; protected $file; function __construct(\Closure $closure) { $this->closure = new \ReflectionFunction($closure); } function __toString() { $prepared_ary = $this->prepare(); $dump_ary = explode('=>', $this->trim(array_shift($prepared_ary))); $function_ary = array_slice($dump_ary, 1); return $this->trim(array_shift($function_ary)); } protected function trim($string) { return trim($string, self::SPLITTERS); } protected function prepare() { $this->file = $this->closure->getFileName(); $this->begin = $this->closure->getStartLine() - 1; $this->end = $this->closure->getEndLine(); return array_slice(file($this->file), $this->begin, $this->end - $this->begin); } } Входные данные: $functions = [ 'foo' => function() { return 'foo'; }, 'bar' => function() { return 'bar'; }, ]; Ну и результат: echo new ClosureDump($functions['foo']); // => function() { return 'foo'; } UPD: Чуть позже нашел более полноценную реализацию. UPD2: class MyClosure { const EOL = PHP_EOL; const TAB = ' '; protected $reflection; protected $closure; # interface public function __construct(\Closure $closure) { $this->setClosure($closure); } public function getClosure() { return $this->closure; } public function setClosure(\Closure $closure) { $this->closure = $closure; $this->reflection = new \ReflectionFunction($closure); } public function __toString() { return $this->buildSignature() . $this->buildBody() . $this->buildTail(); } # protected protected function buildSignature() { $params = []; foreach($this->reflection->getParameters() as $param) { $str = ''; if ($param->isArray()) { $str .= 'array '; } else if ($param->getClass()) { $str .= $param->getClass()->name . ' '; } if ($param->isPassedByReference()){ $str .= '&'; } $str .= '$' . $param->name; if ($param->isOptional()) { $str .= ' = ' . var_export($param->getDefaultValue(), true); } $params[] = $str; } $ary = ['function (', implode(', ', $params), ') {' . self::EOL]; return implode($ary); } protected function buildBody() { $reflection = $this->reflection; $path = $reflection->getFileName(); $begin = $reflection->getStartLine(); $end = $reflection->getEndLine() - 1; if ($end > $begin) { $ary = array_slice(file($path), $begin, $end - $begin); } else { throw new \LogicException; } return implode(array_map(function ($line) { return self::TAB . trim($line) . self::EOL; }, $ary)); } protected function buildTail() { return '}'; } } $ary = [ 'foo' => function() { return 'foo'; }, 'bar' => function($foo, $bar, & $baz) { $baz = $foo . $bar; return $baz; }, ]; $closure = new myClosure($ary['bar']); Результат вывода $closure: function ($foo, $bar, &$baz) { $baz = $foo . $bar; return $baz; } После небольшого рефакторинга...Ответ 2
Все ребят всем спасибо, разабрался, теперь работает как надо добавил метод: private function arr(array $array) { if(sizeof($array)) { $arr = array(); foreach($array as $key => $value) { if(is_callable($value)) { $value = new MyClosure($value); $value = $value->__toString(); $arr[] = "'{$key}' => {$value}"; } } return'array(' . join(',', $arr) . ');'; } return false; } И заработало, меняется туда сюда, скрипт-код и сервер по прежнему живет и видно как добавляем и изменяем, Огромнейшее спасибо, без вас бы я не справился ;)Ответ 3
Пришлось делать запись данных уже в нужном виде (если посоветуете, что-то, лучше, буду рад): public final function arrayDump(array $array): string { if (1 <= sizeof($array)) { $arr = []; foreach ($array as $key => $value) { $key = addslashes($key); if (is_callable($value)) { $value = $this->closure($value); $arr[] = "'{$key}' => {$value}"; } else if (is_string($value)) { $arr[] = "'{$key}' => '" . addslashes($value) . "'"; } else if (is_float($value) || is_double($value)) { $value = (double) $value; $arr[] = "'{$key}' => {$value}"; } else if (is_int($value)) { $value = (int) $value; $arr[] = "'{$key}' => {$value}"; } else if (is_object($value)) { $arr[] = "'{$key}' => '" . serialize($value) . "'"; } } return"[\n\t" . join("\t", $arr) . "\n];"; } return''; } Ну и сам метод closure public final function closure(Closure $closure): string { $func = 'function('; $function = new ReflectionFunction($closure); $params = []; $parameters = $function->getParameters(); foreach ($parameters as $param) { $string = ''; if ($param->isArray()) { $string .= 'array '; } else if ($param->getClass()) { $string .= $param->getClass()->name . ' '; } if ($param->isPassedByReference()) { $string .= '&'; } $string .= '$' . $param->name; if ($param->isOptional()) { $string .= ' = ' . var_export($param->getDefaultValue(), true); } $params[] = $string; } unset($parameters, $param); $func .= join(', ', $params) . ') {' . PHP_EOL; $lines = file($function->getFileName()); $start = $function->getStartLine(); $end = $function->getEndLine(); for ($l = $start; $l < $end; ++$l) { $func .= $lines[$l]; } return $func; } Теперь нормально работает.Ответ 4
Редактирование норм, но сохранение теперь кривится, сохраняя вида: array( 'foo'=> MyClosure::__set_state(array( 'reflection'=> ReflectionFunction::__set_state(array( 'name'=>'{closure}', )), 'closure'=> Closure::__set_state(array( )), )) ); Затем при импорте и обращении к функции вылетает крит: Fatal error: Call to undefined method MyClosure::__set_state() in ... Нужно, чтобы массив имел вид: array( 'foo'=>function() { return 'foo'; }, ); Видимо это так var_export влияет, есть мысли как его обратно записать в файл массив? Вида: foo.php: return array( 'foo'=>function() { return 'foo'; }, ); Конечно можно попробовать дописать костыль, аналог var_export, но может есть другой выход?
Комментариев нет:
Отправить комментарий