public function testBugCompileEmptyFunction() { Artifex::execute(<<<'EOF' #* function foo() #* end EOF ); $this->assertTrue(true); }
/** * @dataProvider fixturesProvider */ public function testFixtures($file, $expected) { $vm = Artifex::load($file); $this->assertEquals($expected, $vm->run()); }
protected function compile() { $this->readFilters(); $this->createUrlObjects(); $groups = $this->groupByMethod($this->urls); $groups->iterate(array($this, 'groupByPartsSize')); $groups->iterate(array($this, 'groupByPatterns')); $groups->sort(function ($obj1, $obj2) { return $obj1->getWeight() - $obj2->getWeight(); }); $config = $this->config; $output = $this->config->getOutput(); $self = $this; $args = compact('self', 'groups', 'config', 'complex'); $vm = \Artifex::load(__DIR__ . '/Template/Main.tpl.php', $args); $vm->doInclude('Switch.tpl.php'); $vm->doInclude('Url.tpl.php'); $vm->doInclude('If.tpl.php'); /** * Convert every Url or UrlGroup object into * code :-) */ $vm->registerFunction('render', function ($obj) use($vm) { if ($obj instanceof UrlGroup_Switch) { $fnc = 'render_group'; } else { if ($obj instanceof UrlGroup_If) { $fnc = 'render_if'; } else { if ($obj instanceof Url) { $fnc = 'render_url'; } else { if (is_array($obj)) { $fnc = $vm->getFunction('render'); $buf = ''; foreach ($obj as $url) { $buf .= $fnc($url); } return $buf; } else { throw new \RuntimeException("Don't know how to render " . get_class($obj)); } } } } $fnc = $vm->getFunction($fnc); return $fnc($obj); }); $vm->registerFunction('callback_object', $callback = function ($annotation) use($vm, $self, $output) { if ($annotation->isFunction()) { return var_export('\\' . $annotation['function'], true); } else { if ($annotation->isMethod()) { $class = "\\" . $annotation['class']; $obj = "\$obj_filt_" . substr(sha1($class), 0, 8); return "array({$obj}, " . var_export($annotation['function'], true) . ')'; } else { throw new \RuntimeException("Invalid callback"); } } }); /** * Generate the callback function (from a function or * a method) */ $vm->registerFunction('callback', $callback = function ($annotation) use($vm, $self, $output) { $fileHash = '$file_' . substr(sha1($annotation['file']), 0, 8); $filePath = $annotation['file']; if (!empty($output)) { $filePath = Path::getRelative($annotation['file'], $output); } // prepare loading of the method/function // by doing this we avoid the need of having an "autoloader", // also autoloaders doesn't work with functions, this solution does. $vm->printIndented("if (empty({$fileHash})) {\n"); $vm->printIndented(" {$fileHash} = 1;\n"); if (!empty($output)) { $vm->printIndented(' require_once __DIR__ . "/' . addslashes($filePath) . '";' . "\n"); } else { $vm->printIndented(' require_once "' . addslashes($filePath) . '";' . "\n"); } $vm->printIndented("}\n"); // Get Code representation out of arguments array $args = func_get_args(); array_shift($args); $args = array_map(function ($param) { $param = is_scalar($param) ? (string) $param : $param; $text = !empty($param[0]) && $param[0] == '$' ? $param : var_export($param, true); return $text; }, $args); // check if the filter is cachable switch (count($args)) { case 3: $cache = intval($annotation->getOne('Cache')); break; case 1: $zargs = $annotation->getMetadata(); $zargs = $zargs['params']; for ($i = 1; $i < count($zargs); $i++) { $args[] = '$req->get(' . var_export(substr($zargs[$i], 1), true) . ')'; } break; } $arguments = implode(", ", $args); if ($annotation->isFunction()) { // generate code for functions $function = "\\" . $annotation['function']; if (!empty($cache)) { return '$this->doCachedFilter(' . var_export($function, true) . ", {$arguments}, {$cache})"; } else { return "{$function}({$arguments})"; } } else { if ($annotation->isMethod()) { // It is a method, *for now* we don't care if the method // is static so we instanciate an object if it wasn't done before $class = "\\" . $annotation['class']; $method = $annotation['function']; $obj = "\$obj_filt_" . substr(sha1($class), 0, 8); $vm->printIndented("if (empty({$obj})) {\n"); $vm->printIndented(" {$obj} = new {$class};\n"); $vm->printIndented("}\n"); if (!empty($cache)) { return '$this->doCachedFilter(array(' . "{$obj}, '{$method}'), {$arguments}, {$cache})"; } else { return "{$obj}->{$method}({$arguments})"; } } else { throw new \RuntimeException("Invalid callback"); } } }); /** * Generate expressions */ $vm->registerFunction('expr', function ($rules) use($self, $callback) { if (!is_array($rules)) { $rules = array($rules); } if (count($rules) == 0) { return ''; } $expr = array(); foreach ($rules as $rule) { $expr[] = $rule->getExpr($self, $callback) ?: ''; } return implode(' && ', array_filter($expr)); }); $this->output = $vm->run(); //$this->output = FixCode::fix($vm->run()); }
<?php require "lib/Artifex.php"; Artifex::registerAutoloader(); function safe_eval($code) { $namespace = "Artifex\\test\\x" . uniqid(true); eval("namespace {$namespace} ;\n " . $code); return $namespace; }
public function testDefineScope() { $vm = Artifex::compile(<<<'EOF' #* $foo = "foo" #* $methods = [1, 2] #* function defineClass($foo, $methods) class __foo__ { #* foreach ($methods as $method) public function set__method__() { } #* end } #* end EOF ); $function = $vm->getFunction("defineClass"); try { /* it fails because the main scope haven't run yet and the variables $foo and $methods doens't exists yet */ $code = $function(); $this->assertTrue(false); } catch (\RuntimeException $e) { $this->assertTrue(true); } $vm->run(); $code = $function(); $namespace = safe_eval($code); $class = $namespace . '\\foo'; $this->assertTrue(class_exists($class)); $this->assertTrue(is_callable($class, 'set1')); $this->assertTrue(is_callable($class, 'set2')); }
public function doInclude($tpl) { $file = $this->getPath($tpl); $vm = \Artifex::load($file, $this->variables); $fnc = array_merge($this->functions, $vm->functions); $this->functions = $fnc; $vm->functions = $fnc; return $vm->run(); }