#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, но может есть другой выход?
Комментариев нет:
Отправить комментарий