parser = new PHPParser_Parser(new PHPParser_Lexer); $this->nodeDumper = new PHPParser_NodeDumper; $this->prettyPrinter = new PHPParser_PrettyPrinter_Zend; $this->ast_dump_before_walk=""; $this->ast_dump_after_walk=""; } public function setASTDumpFiles($ast_dump_before_walk, $ast_dump_after_walk) { // Set file to "" to prevent AST dumping $this->$ast_dump_before_walk=$ast_dump_before_walk; $this->$ast_dump_after_walk=$ast_dump_after_walk; } public function runAllSteps($sourcetree_rootpath, $cache_path, $result_path) { try { $step='detectFramework'; $fw_props=$this->detectFramework($sourcetree_rootpath); $step='parseFrameworkConfig'; $fw_conf=$this->parseFrameworkConfig($fw_props); $step='parseFrameworkCode'; $fw_extra=$this->parseFrameworkCode($fw_props, $fw_conf, $cache_path); $step='parseApplicationCode'; $app_extra=$this->parseApplicationCode($fw_props, $fw_conf, $fw_extra, $cache_path); $step='computePageList'; $app_pages=$this->computePageList($fw_props, $fw_conf, $fw_extra, $app_extra); $step='outputMappingFile'; $this->outputMappingFile($app_pages, $result_path); foreach ($app_pages as $page=>$page_props) { $step="makePageFullAST of '$page'"; $page_full_ast=$this->makePageFullAST($page, $page_props, $fw_props, $fw_conf, $fw_extra, $app_extra, $cache_path); $step="analysePageAST of '$page'"; $page_annotated_ast=$this->analysePageAST($page_full_ast, $page, $page_props, $fw_props, $fw_conf, $fw_extra, $app_extra, $cache_path); $step="throwDeadCode of '$page'"; $this->throwDeadCode($page_annotated_ast); $step="weaveCode of '$page'"; $out_ast=$this->weaveCode($page_annotated_ast); $page_dest_path=$this->makeDestPath($page, $result_path); $step="prettyPrint to '$page_dest_path'"; $this->prettyPrint($out_ast, $page_dest_path); } } catch (Exception $e) { echo "Step '$step' failure : ", $e->getMessage(); } } 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. * The main call should be in this case : * $w=new XXXWeaver; * $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)); */ try { dbg($level,"Parsing '$src_filepath'"); $stmts = $this->parser->parse(file_get_contents($src_filepath)); $this->dumpAST($stmts, $src_filepath, $this->ast_dump_before_walk); if ($traverser) { dbg($level,"Transforming '$src_filepath'"); $stmts = $traverser->traverse($stmts); $traverser=null; //Destroy } $this->dumpAST($stmts, $src_filepath, $this->ast_dump_after_walk); } catch (PHPParser_Error $e) { echo 'Parse Error: ', $e->getMessage(); } 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"); } public function dumpAST($ast, $ast_title, $dest_filepath) { if (is_array($ast) && strlen($dest_filepath) > 0 ) { dbg($level,"Dumping '$ast_title,' AST to '$dest_filepath'"); file_put_contents($dest_filepath, $this->nodeDumper->dump($ast)); } } // Framework specific code abstract public function detectFramework($sourcetree_rootpath); abstract public function parseFrameworkConfig($fw_props); abstract public function parseFrameworkCode($fw_props, $fw_conf, $cache_path); abstract public function parseApplicationCode($fw_props, $fw_conf, $fw_extra, $cache_path); abstract public function computePageList($fw_props, $fw_conf, $fw_extra, $app_extra); abstract public function outputMappingFile($app_pages, $result_path); abstract public function makePageFullAST($page, $page_props, $fw_props, $fw_conf, $fw_extra, $app_extra, $cache_path); abstract public function analysePageAST($page_full_ast, $page, $page_props, $fw_props, $fw_conf, $fw_extra, $app_extra, $cache_path); abstract public function throwDeadCode(&$page_annotated_ast); abstract public function weaveCode($page_annotated_ast); }