From 688d58f51120786b099509ea3785a8057ae36265 Mon Sep 17 00:00:00 2001 From: Ludovic Pouzenc Date: Thu, 2 Aug 2012 21:49:31 +0000 Subject: Implémentation pour Cake : étape 1 sur 124684 réalisée : Fonction detectFramework() implementée. Parse le app/webroot/index.php pour récurérer tous les define() et les évalue statiquement et ça marche ! MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: file:///var/svn/2012-php-weave/trunk@10 d972a294-176a-4cf9-8ea1-fcd5b0c30f5c --- .../src/php-weave/abstract_weaver.class.php | 106 ++++++++++++++++++++- .../src/php-weave/cakephp_weaver.class.php | 16 ++++ 2 files changed, 119 insertions(+), 3 deletions(-) diff --git a/poc/poc02-compiling-cake/src/php-weave/abstract_weaver.class.php b/poc/poc02-compiling-cake/src/php-weave/abstract_weaver.class.php index 86a07b1..cebe62a 100644 --- a/poc/poc02-compiling-cake/src/php-weave/abstract_weaver.class.php +++ b/poc/poc02-compiling-cake/src/php-weave/abstract_weaver.class.php @@ -64,12 +64,14 @@ abstract class AbstractWeaver { public function makeDestPath($page, $result_path) { return $result_path . '/' . str_replace('/', '_', $page) . ".php"; } + public function parseAndWalk($src_filepath, $traverser, $env=array(), $level=0) { /* Parse a file et traverse the AST. - * Could be recursive if a traverser call again parseAndWalk for example when finding a include() directive + * Could be recursive if a traverser call again parseAndWalk + * for example when finding a include() directive. * The main call should be in this case : * $w=new XXXWeaver; - * $nv=new NodeVisitor_XXX($w); // The node visitor + * $nv=new NodeVisitor_YYY($w); // The node visitor * $t=new PHPParser_NodeTraverser; $t->addVisitor($nv); * w->parseAndWalk($src_mainfile, $t, array('cwd'=>dirname($src_mainfile)); */ @@ -93,9 +95,107 @@ abstract class AbstractWeaver { return $stmts; } + public function staticEvalDefine($ast, $magics, $previous_constants=null) { + if ($previous_constants==null) { + $constants=array('DIRECTORY_SEPARATOR' => DIRECTORY_SEPARATOR); + } else { + $constants=$previous_constants; + } + while($stmt=array_shift($ast)) { + if ($stmt instanceof PHPParser_Node_Stmt_If) { + //TODO try to eval statically the cond + if (1) { + $constants += $this->staticEvalDefine($stmt->stmts, $magics, $constants); + } + } elseif ($stmt instanceof PHPParser_Node_Expr_FuncCall) { + if ( $stmt->name->parts === array('define') ) { + $k=$this->staticReduce($stmt->args[0], $constants, $magics); + $v=$this->staticReduce($stmt->args[1], $constants, $magics); + if ($k instanceof PHPParser_Node_Scalar_String && $v instanceof PHPParser_Node_Scalar_String) { + $k=$k->value; + $v=$v->value; + dbg(0,"Found '$k' => '$v'"); + $constants[$k]=$v; + } + } else { + dbg(0,"Skipped funcall: " . $this->prettyPrinter->prettyPrint(array($stmt))); + } + } else { + dbg(0,"Skipped : " . get_class($stmt)); + } + } + return $constants; + } + + public function staticReduce($stmt, $constants, $magics, $level=0) { + //dbg($level,'staticReduce(<'.get_class($stmt).'>, $constants, $magics)'); + + // Condition de sortie + if ($stmt instanceof PHPParser_Node_Scalar_String) return $stmt; + // Propagation d'erreur + if (is_integer($stmt)) return $stmt; + + // Cas nominal, action selon type de noeud + if ($stmt instanceof PHPParser_Node_Expr_FuncCall) { + // Support some string manipulation funcs + if ( count($stmt->name->parts)===1 ) { + $func=$stmt->name->parts[0]; + switch($func) { + // Unary context insensitive funcs + case 'dirname': + case 'basename': + $arg=$this->staticReduce($stmt->args[0], $constants, $magics, $level+1); + if ($arg instanceof PHPParser_Node_Scalar_String) { + $res=$func($arg->value); //Real call !! + return new PHPParser_Node_Scalar_String($res); + } + break; + default: + dbg(0,"Unsupported func '$func'"); + return 1; + } + } else { + dbg(0,"Unknown multipart funcname"); + return 2; + } + } elseif ($stmt instanceof PHPParser_Node_Arg) { + // Unbox arguments, ignoring by ref things (static context) + return $this->staticReduce($stmt->value, $constants, $magics, $level+1); + } elseif ($stmt instanceof PHPParser_Node_Expr_Concat) { + $l=$this->staticReduce($stmt->left, $constants, $magics, $level+1); + if (! $l instanceof PHPParser_Node_Scalar_String) { + return $l; + } + $r=$this->staticReduce($stmt->right, $constants, $magics, $level+1); + if (! $r instanceof PHPParser_Node_Scalar_String) { + return $r; + } + return new PHPParser_Node_Scalar_String($l->value . $r->value); + } elseif ($stmt instanceof PHPParser_Node_Expr_ConstFetch) { + //TODO : a ConstFetch could be have more than one part in his name ? + // What glue between pieces ?? + //$k=$this->prettyPrinter->prettyPrint(array($stmt)); + $k=$stmt->name->parts[0]; + if (array_key_exists($k, $constants)) { + return new PHPParser_Node_Scalar_String($constants[$k]); + } else { + dbg(0,"ConstFetch failed for '$k'"); + return 3; + } + } elseif ($stmt instanceof PHPParser_Node_Scalar_FileConst) { + $k='__FILE__'; + if (array_key_exists($k, $magics)) { + return new PHPParser_Node_Scalar_String($magics[$k]); + } else { + dbg(0,"Magic evaluation failed for '$k'"); + return 4; + } + } + } + public function prettyPrint($ast, $dest_filepath) { dbg(0,"Outputing '$dest_filepath'"); - file_put_contents($dest_filepath, "prettyPrinter->prettyPrint($ast) . "\n?>"); + file_put_contents($dest_filepath, "prettyPrinter->prettyPrint($ast) . "\n"); } public function dumpAST($ast, $ast_title, $dest_filepath) { diff --git a/poc/poc02-compiling-cake/src/php-weave/cakephp_weaver.class.php b/poc/poc02-compiling-cake/src/php-weave/cakephp_weaver.class.php index cc25641..508e509 100644 --- a/poc/poc02-compiling-cake/src/php-weave/cakephp_weaver.class.php +++ b/poc/poc02-compiling-cake/src/php-weave/cakephp_weaver.class.php @@ -3,7 +3,23 @@ require_once 'abstract_weaver.class.php'; class CakePHPWeaver extends AbstractWeaver { public function detectFramework($sourcetree_rootpath) { + //FIXME : there is other cases to cover + $entrypoint_path=$sourcetree_rootpath.'/app/webroot/index.php'; + assert('is_readable($entrypoint_path)'); + $entrypoint_ast=$this->parseAndWalk($entrypoint_path, null); + //echo $this->nodeDumper->dump($entrypoint_ast); + + $magics=array('__FILE__' => $entrypoint_path); + $fw_props=$this->staticEvalDefine($entrypoint_ast, $magics); + if (!is_array($fw_props)) throw new Exception("No properties found in '$entrypoint_path'"); + + print_r($fw_props); + $required=array('ROOT','APP_DIR','CAKE_CORE_INCLUDE_PATH','WEBROOT_DIR', 'WWW_ROOT'); + $missing=array_diff($required, array_keys($fw_props)); + print_r($missing); + assert('count($missing)===0;'); + return array_merge(array('entrypoint_path' => $entrypoint_path), $fw_props); } public function parseFrameworkConfig($fw_props) { -- cgit v1.2.3